diff --git a/kb-app-demo-desktop/src/demo_decode_replay.rs b/kb-app-demo-desktop/src/demo_decode_replay.rs
index fd1dd8f..abd4696 100644
--- a/kb-app-demo-desktop/src/demo_decode_replay.rs
+++ b/kb-app-demo-desktop/src/demo_decode_replay.rs
@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
-// version: 44
+// version: 45
//! Tauri commands and UI payloads for contextual instruction decode replay.
@@ -605,7 +605,7 @@ pub(crate) async fn demo_decode_replay_annotations(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoTransactionAnnotationRequest,
) -> std::result::Result, std::string::String> {
- let filter_result = ks_store::MaterializedEventFilter::new(
+ let filter_result = ks_store::MaterializedOutputFilter::new(
std::option::Option::Some("materializer.transaction.annotations".to_string()),
std::option::Option::Some("transaction_annotation".to_string()),
request.signature_contains,
@@ -622,7 +622,7 @@ pub(crate) async fn demo_decode_replay_annotations(
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result =
- ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await;
+ ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
@@ -882,7 +882,7 @@ fn coverage_payload(
}
fn annotation_payload(
- row: ks_store::MaterializedEventQueryRow,
+ row: ks_store::MaterializedOutputQueryRow,
) -> std::result::Result {
if row.processor_name != "materializer.transaction.annotations"
|| row.materialized_family != "transaction_annotation"
@@ -1274,7 +1274,7 @@ mod tests {
#[test]
fn committed_annotation_row_is_mapped_to_ui_safe_contract() {
- let row = ks_store::MaterializedEventQueryRow {
+ let row = ks_store::MaterializedOutputQueryRow {
processor_name: "materializer.transaction.annotations".to_string(),
processor_version: "0.4.3".to_string(),
input_key: "input".to_string(),
@@ -1310,7 +1310,7 @@ mod tests {
#[test]
fn malformed_annotation_payload_fails_closed() {
- let row = ks_store::MaterializedEventQueryRow {
+ let row = ks_store::MaterializedOutputQueryRow {
processor_name: "materializer.transaction.annotations".to_string(),
processor_version: "0.4.3".to_string(),
input_key: "input".to_string(),
diff --git a/kb-app-demo-desktop/src/demo_spl_ata.rs b/kb-app-demo-desktop/src/demo_spl_ata.rs
index 412685a..0634a89 100644
--- a/kb-app-demo-desktop/src/demo_spl_ata.rs
+++ b/kb-app-demo-desktop/src/demo_spl_ata.rs
@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
-// version: 18
+// version: 19
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
@@ -316,7 +316,7 @@ pub(crate) async fn demo_spl_ata_journal(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::Some("materializer.token.accounts".to_string()),
std::option::Option::None,
request.signature_contains.clone(),
@@ -325,7 +325,7 @@ pub(crate) async fn demo_spl_ata_journal(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
@@ -560,7 +560,7 @@ fn journal_matches(payload: &serde_json::Value, request: &crate::DemoSplAtaJourn
return true;
}
-fn journal_row(row: ks_store::MaterializedEventQueryRow) -> crate::DemoSplAtaJournalRow {
+fn journal_row(row: ks_store::MaterializedOutputQueryRow) -> crate::DemoSplAtaJournalRow {
let text = |key: &str| {
return row
.payload_json
diff --git a/kb-app-demo-desktop/src/demo_spl_token.rs b/kb-app-demo-desktop/src/demo_spl_token.rs
index 0a0eaa2..578ecc9 100644
--- a/kb-app-demo-desktop/src/demo_spl_token.rs
+++ b/kb-app-demo-desktop/src/demo_spl_token.rs
@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_token.rs
-// version: 16
+// version: 17
//! Thin Tauri adapter for classic SPL Token execution and materialized journals.
@@ -263,11 +263,11 @@ pub(crate) async fn demo_spl_token_journal(
let needs_payload_filter =
validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some();
let query_limit = if needs_payload_filter {
- ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS
+ ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS
} else {
validated.limit
};
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
validated.family.clone(),
validated.signature_contains.clone(),
@@ -276,7 +276,7 @@ pub(crate) async fn demo_spl_token_journal(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
@@ -412,10 +412,10 @@ fn replay_diagnostics(summary: &ks_pipeline::DecodeReplaySummary) -> serde_json:
fn validate_journal_request(
request: crate::DemoSplTokenJournalRequest,
) -> std::result::Result {
- if request.limit == 0 || request.limit > ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS {
+ if request.limit == 0 || request.limit > ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS {
return std::result::Result::Err(format!(
"journal limit must be between 1 and {}",
- ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS
+ ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS
));
}
if request.profile_name.trim().is_empty() {
@@ -496,7 +496,7 @@ fn payload_matches(
return true;
}
-fn journal_row(row: ks_store::MaterializedEventQueryRow) -> DemoSplTokenJournalRow {
+fn journal_row(row: ks_store::MaterializedOutputQueryRow) -> DemoSplTokenJournalRow {
let operation = row
.payload_json
.get("operation")
@@ -602,7 +602,7 @@ mod tests {
payload.get("amountRaw").and_then(serde_json::Value::as_str),
std::option::Option::Some("18446744073709551615")
);
- let row = super::journal_row(ks_store::MaterializedEventQueryRow {
+ let row = super::journal_row(ks_store::MaterializedOutputQueryRow {
processor_name: "materializer.token.accounts".to_string(),
processor_version: "0.4.4".to_string(),
input_key: "input".to_string(),
diff --git a/ks-lib/CHANGELOG.md b/ks-lib/CHANGELOG.md
index acdf076..92995d1 100644
--- a/ks-lib/CHANGELOG.md
+++ b/ks-lib/CHANGELOG.md
@@ -1,8 +1,14 @@
-
+
# CHANGELOG — ks-lib
+## `0.5.3-pre.003`
+
+- fait évoluer `MdCoreInstructionReplayInput` vers le contrat version 3 afin de transporter le contexte Core nécessaire aux futurs décodeurs : `block_time`, `stack_height`, parent CPI et `returnData` transactionnelle ;
+- renomme le contexte de transaction `outer_instructions_json` en `top_level_instructions_json` dans ce nouveau contrat store/replay, sans supprimer les usages historiques du terme `outer` lorsqu'ils décrivent encore explicitement une sémantique de décodeur existante ;
+- conserve le constructeur historique de replay pour les tests/sources synthétiques et ajoute l'enrichissement `with_core_context` utilisé par `ks-store`.
+
## `0.5.1-pre.008`
- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ;
diff --git a/ks-lib/src/decoder/api/contracts.rs b/ks-lib/src/decoder/api/contracts.rs
index aef0076..e6e280b 100644
--- a/ks-lib/src/decoder/api/contracts.rs
+++ b/ks-lib/src/decoder/api/contracts.rs
@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/api/contracts.rs
-// version: 9
+// version: 10
//! Backend-neutral contextual instruction decoder contracts.
@@ -511,7 +511,7 @@ mod tests {
fn contextual_input_hash_changes_when_outer_context_changes() {
let first = replay_input();
let mut second = replay_input();
- second.outer_instructions_json = serde_json::json!([{
+ second.top_level_instructions_json = serde_json::json!([{
"instructionIndex": 0,
"instructionPath": "0",
"programId": "program",
diff --git a/ks-lib/src/decoder/solana/core/compute_budget.rs b/ks-lib/src/decoder/solana/core/compute_budget.rs
index e406cb2..afddf58 100644
--- a/ks-lib/src/decoder/solana/core/compute_budget.rs
+++ b/ks-lib/src/decoder/solana/core/compute_budget.rs
@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/compute_budget.rs
-// version: 7
+// version: 8
//! Maximal current and historical Compute Budget instruction decoding.
@@ -341,7 +341,7 @@ fn compute_budget_outer_rows(
input: &crate::MdCoreInstructionReplayInput,
) -> std::vec::Vec {
let mut rows = std::vec::Vec::new();
- let outer_values = match input.outer_instructions_json.as_array() {
+ let outer_values = match input.top_level_instructions_json.as_array() {
std::option::Option::Some(value) => value,
std::option::Option::None => return rows,
};
@@ -705,7 +705,7 @@ mod tests {
fn replay_input_with_outer(
bytes: &[u8],
instruction_path: &str,
- outer_instructions_json: serde_json::Value,
+ top_level_instructions_json: serde_json::Value,
) -> crate::MdCoreInstructionReplayInput {
let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
let result = crate::MdCoreInstructionReplayInput::new(
@@ -720,7 +720,7 @@ mod tests {
serde_json::json!([]),
std::option::Option::Some(serde_json::json!({"dataBase64": encoded})),
std::option::Option::Some("payload-hash".to_string()),
- outer_instructions_json,
+ top_level_instructions_json,
serde_json::json!([]),
serde_json::json!([]),
serde_json::json!([]),
diff --git a/ks-lib/src/decoder/solana/core/payload.rs b/ks-lib/src/decoder/solana/core/payload.rs
index 21a0433..88dc8b8 100644
--- a/ks-lib/src/decoder/solana/core/payload.rs
+++ b/ks-lib/src/decoder/solana/core/payload.rs
@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/payload.rs
-// version: 7
+// version: 8
//! Bounded native instruction payload helpers.
@@ -228,7 +228,7 @@ fn resolve_explicit_outer_instruction(
input: &crate::MdCoreInstructionReplayInput,
instruction_index: usize,
) -> ks_core::Result {
- let entries = match input.outer_instructions_json.as_array() {
+ let entries = match input.top_level_instructions_json.as_array() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ks_core::Error::invalid_state(
diff --git a/ks-lib/src/decoder/solana/core/precompiles.rs b/ks-lib/src/decoder/solana/core/precompiles.rs
index 1dfb521..3f91f6b 100644
--- a/ks-lib/src/decoder/solana/core/precompiles.rs
+++ b/ks-lib/src/decoder/solana/core/precompiles.rs
@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/precompiles.rs
-// version: 6
+// version: 7
//! Bounded structural decoding for Solana signature verification precompiles.
@@ -1599,11 +1599,11 @@ mod tests {
for (program_id, target, external) in cases {
let mut missing =
replay_input(program_id, target.clone(), vec![(0, external.clone())], false);
- missing.outer_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
+ missing.top_level_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
let missing_result = crate::decoder_solana_core_precompiles_decode(&missing);
assert_eq!(missing_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
let mut invalid = replay_input(program_id, target, vec![(0, external)], false);
- invalid.outer_instructions_json[0]["payloadJson"] =
+ invalid.top_level_instructions_json[0]["payloadJson"] =
serde_json::json!({"dataBase64": "%%%"});
let invalid_result = crate::decoder_solana_core_precompiles_decode(&invalid);
assert_eq!(invalid_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
diff --git a/ks-lib/src/decoder/solana/core/slashing.rs b/ks-lib/src/decoder/solana/core/slashing.rs
index 1c3b98b..c1f3773 100644
--- a/ks-lib/src/decoder/solana/core/slashing.rs
+++ b/ks-lib/src/decoder/solana/core/slashing.rs
@@ -1,5 +1,5 @@
// file: ks-lib/src/decoder/solana/core/slashing.rs
-// version: 4
+// version: 5
//! Exact structural decoding for the enshrined stateless Slashing Program.
@@ -413,7 +413,7 @@ fn preceding_ed25519_instruction(input: &crate::MdCoreInstructionReplayInput) ->
});
},
};
- let previous = input.outer_instructions_json.as_array().and_then(|instructions| {
+ let previous = input.top_level_instructions_json.as_array().and_then(|instructions| {
return instructions.iter().find(|instruction| {
return instruction
.get("instructionIndex")
diff --git a/ks-lib/src/model/replay.rs b/ks-lib/src/model/replay.rs
index 68c3679..28a1bcc 100644
--- a/ks-lib/src/model/replay.rs
+++ b/ks-lib/src/model/replay.rs
@@ -1,10 +1,10 @@
// file: ks-lib/src/model/replay.rs
-// version: 4
+// version: 5
//! Source-neutral instruction replay input shared by decoders and stores.
/// Version of the normalized core replay contract.
-pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 2;
+pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 3;
/// Decoder replay input containing one instruction plus extracted transaction context.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -17,8 +17,14 @@ pub struct MdCoreInstructionReplayInput {
pub signature: std::string::String,
/// Transaction slot in Solana unsigned representation.
pub slot: u64,
+ /// Optional block timestamp as Unix seconds supplied by Solana.
+ pub block_time: std::option::Option,
/// Stable instruction path, for example `0` or `2/1`.
pub instruction_path: std::string::String,
+ /// Optional immediate or best-known parent instruction path for CPI inputs.
+ pub parent_instruction_path: std::option::Option,
+ /// Optional runtime invocation stack height.
+ pub stack_height: std::option::Option,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
/// Optional surface hint supplied by an operator or upstream classifier.
@@ -35,8 +41,10 @@ pub struct MdCoreInstructionReplayInput {
pub instruction_payload_json: std::option::Option,
/// Optional deterministic hash of the retained instruction payload.
pub instruction_payload_hash: std::option::Option,
- /// Ordered outer instructions for the transaction.
- pub outer_instructions_json: serde_json::Value,
+ /// Optional transaction return-data fact retained by Core.
+ pub return_data: std::option::Option,
+ /// Ordered top-level instructions for the transaction.
+ pub top_level_instructions_json: serde_json::Value,
/// Inner instruction tree or subtree relevant to the target instruction.
pub inner_instructions_json: serde_json::Value,
/// Ordered logs relevant to the transaction or target instruction.
@@ -45,7 +53,7 @@ pub struct MdCoreInstructionReplayInput {
pub balance_changes_json: serde_json::Value,
}
-impl MdCoreInstructionReplayInput {
+impl crate::MdCoreInstructionReplayInput {
/// Builds a decoder replay input after validating its stable identity and array context.
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -60,14 +68,14 @@ impl MdCoreInstructionReplayInput {
instruction_accounts_json: serde_json::Value,
instruction_payload_json: std::option::Option,
instruction_payload_hash: std::option::Option,
- outer_instructions_json: serde_json::Value,
+ top_level_instructions_json: serde_json::Value,
inner_instructions_json: serde_json::Value,
logs_json: serde_json::Value,
balance_changes_json: serde_json::Value,
) -> ks_core::Result {
- if !outer_instructions_json.is_array() {
+ if !top_level_instructions_json.is_array() {
return std::result::Result::Err(ks_core::Error::invalid_state(
- "core replay input outer instructions must be a JSON array",
+ "core replay input top-level instructions must be a JSON array",
));
}
let value = Self {
@@ -75,7 +83,10 @@ impl MdCoreInstructionReplayInput {
replay_input_key: replay_input_key.into(),
signature: signature.into(),
slot,
+ block_time: std::option::Option::None,
instruction_path: instruction_path.into(),
+ parent_instruction_path: std::option::Option::None,
+ stack_height: std::option::Option::None,
program_id: program_id.into(),
surface_code_hint: std::option::Option::None,
transaction_failed,
@@ -84,7 +95,8 @@ impl MdCoreInstructionReplayInput {
instruction_accounts_json,
instruction_payload_json,
instruction_payload_hash,
- outer_instructions_json,
+ return_data: std::option::Option::None,
+ top_level_instructions_json,
inner_instructions_json,
logs_json,
balance_changes_json,
@@ -124,6 +136,47 @@ impl MdCoreInstructionReplayInput {
return std::result::Result::Ok(());
}
+ /// Adds transaction time, CPI ancestry and return-data context retained by Core.
+ pub fn with_core_context(
+ mut self,
+ block_time: std::option::Option,
+ parent_instruction_path: std::option::Option,
+ stack_height: std::option::Option,
+ return_data: std::option::Option,
+ ) -> ks_core::Result {
+ if parent_instruction_path
+ .as_deref()
+ .is_some_and(|value| return value.trim().is_empty())
+ {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "core replay parent instruction path must not be empty when present",
+ ));
+ }
+ if stack_height.is_some_and(|value| return value == 0) {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "core replay stack height must be greater than zero when present",
+ ));
+ }
+ if parent_instruction_path.is_some() && stack_height.is_some_and(|value| return value <= 1)
+ {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "core replay CPI stack height must be greater than one when present",
+ ));
+ }
+ if let std::option::Option::Some(value) = &return_data {
+ if value.program_id.trim().is_empty() {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "core replay return-data program id must not be empty",
+ ));
+ }
+ }
+ self.block_time = block_time;
+ self.parent_instruction_path = parent_instruction_path;
+ self.stack_height = stack_height;
+ self.return_data = return_data;
+ return std::result::Result::Ok(self);
+ }
+
/// Adds an optional stable surface hint after validation.
pub fn with_surface_code_hint(
mut self,
diff --git a/ks-logging/CHANGELOG.md b/ks-logging/CHANGELOG.md
index bd35128..f404c07 100644
--- a/ks-logging/CHANGELOG.md
+++ b/ks-logging/CHANGELOG.md
@@ -1,8 +1,21 @@
-
+
# CHANGELOG — ks-logging
+## `0.5.3-pre.003`
+
+### Fix `pre.003-delta-fix-005`
+
+- ajoute `h2` et `hyper_util` à `WARN` dans les profils standards ; `hyper` reste explicitement filtré à `WARN` ;
+- étend le canari de configuration aux six targets tierces bruyantes : `h2`, `hyper`, `hyper_util`, `rustls`, `rustls_platform_verifier` et `sqlx` ;
+- documente que le statement logging automatique SQLx est désormais désactivé dans `ks-store`, les traces fonctionnelles de requêtes devant être produites par `ks-store` lui-même.
+
+
+- ajoute `rustls`, `rustls_platform_verifier` et `sqlx` à `WARN` dans les `target_filters` des profils standards `local_devnet`, `mainnet_research` et `mainnet` ;
+- aligne l'exemple de configuration et ajoute un test vérifiant cette politique pour chaque profil ;
+- documente que les logs détaillés des dépendances doivent être réactivés par une route explicite, sans changer leurs targets propriétaires.
+
## `0.5.1-pre.008`
- Fix: remplace un ancien fragment de credential Helius utilisé comme canari négatif par le nom synthétique `KS_SECRET_HELIUS_API_KEY`; aucune donnée réelle ou partielle de credential ne doit être conservée dans les tests.
diff --git a/ks-logging/USAGE.md b/ks-logging/USAGE.md
index 4694a2b..a28d78a 100644
--- a/ks-logging/USAGE.md
+++ b/ks-logging/USAGE.md
@@ -1,5 +1,5 @@
-
+
# Utilisation de ks-logging
@@ -58,3 +58,12 @@ match ks_logging::init_logging(&config) {
```
L'initialisation reste globale au processus. Les binaires doivent donc résoudre leur configuration avant le premier appel à `init_logging`.
+
+## Filtres des dépendances tierces
+
+Les routes wildcard `debug` restent destinées aux composants Khadhroony, mais elles ne doivent pas transformer la console et `debug.log` en trace brute des bibliothèques réseau ou SQL.
+
+Les profils standards imposent `WARN` aux targets `h2`, `hyper`, `hyper_util`, `rustls`, `rustls_platform_verifier` et `sqlx`. Les targets tiers restent ceux de leurs crates propriétaires : un événement HTTP/2 reste `h2::*`, un événement Hyper reste `hyper*`, un événement SQLx reste `sqlx::query` et un événement TLS reste `rustls::*`.
+
+`ks-store` désactive en complément le statement logging automatique de SQLx. Les requêtes fonctionnellement importantes doivent être instrumentées par `ks-store` avec son target canonique et des champs structurés, sans dépendre du format de logging d'une bibliothèque tierce. Les warnings/errors des dépendances restent observables dans les profils standards.
+
diff --git a/ks-logging/src/document.rs b/ks-logging/src/document.rs
index ecd8335..a159806 100644
--- a/ks-logging/src/document.rs
+++ b/ks-logging/src/document.rs
@@ -1,5 +1,5 @@
// file: ks-logging/src/document.rs
-// version: 6
+// version: 8
//! Independent logging configuration document loading and validation.
@@ -521,6 +521,32 @@ mod tests {
}
}
+ #[test]
+ fn default_logging_config_suppresses_verbose_dependency_targets() {
+ let mut value = parse_default_value();
+ value["logs_directory"] = serde_json::Value::String("logs".to_string());
+ let raw_json = value_to_json(&value);
+ let document_result = super::parse_logging_json(&raw_json);
+ let document = match document_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => panic!("default logging config must parse: {error}"),
+ };
+ for profile in &document.profiles {
+ for dependency_target in
+ ["h2", "hyper", "hyper_util", "rustls", "rustls_platform_verifier", "sqlx"]
+ {
+ assert!(
+ profile.config.target_filters.iter().any(|filter| {
+ return filter.target == dependency_target && filter.level == "warn";
+ }),
+ "logging profile {} must suppress verbose {} events",
+ profile.name,
+ dependency_target
+ );
+ }
+ }
+ }
+
#[test]
fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() {
assert!(!DEFAULT_LOGGING_CONFIG.contains("KS_SECRET_HELIUS_API_KEY"));
diff --git a/ks-pipeline-demo-scenarios/CHANGELOG.md b/ks-pipeline-demo-scenarios/CHANGELOG.md
index 4b0fa68..5642597 100644
--- a/ks-pipeline-demo-scenarios/CHANGELOG.md
+++ b/ks-pipeline-demo-scenarios/CHANGELOG.md
@@ -1,8 +1,13 @@
-
+
# CHANGELOG — ks-pipeline-demo-scenarios
+## `0.5.3-pre.003`
+
+- adapte les scénarios réutilisables au renommage N3 `MaterializedOutput*` / `list_materialized_outputs` sans modifier les campagnes Devnet ni leurs critères de succès ;
+- conserve la façade `Store` backend-agnostique introduite en `pre.002` pendant la reconstruction physique PostgreSQL de `pre.003`.
+
## `0.5.3-pre.002`
- remplace les constructions `PostgresStore`/`PostgresStoreOptions` des scénarios par `ks_store::Store` et `StoreOpenOptions` ;
diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
index 03cc78d..e6de313 100644
--- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
+++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
-// version: 26
+// version: 27
//! Real Devnet simulation and submission for current Metaplex Token Metadata operations.
@@ -357,7 +357,7 @@ pub struct DevnetMetaplexTokenMetadataExecutionSummary {
/// Second replay proving idempotence for the exact same input.
pub idempotence_replay: std::option::Option,
/// Exact materialized rows produced for the submitted Metaplex transaction.
- pub materializations: std::vec::Vec,
+ pub materializations: std::vec::Vec,
/// Aggregated canonical post-execution diagnostic.
pub post_execution: std::option::Option,
}
@@ -758,7 +758,7 @@ where
.push(replay_summary_diagnostic("first Metaplex replay", &first_replay));
}
summary.decode_replay = std::option::Option::Some(first_replay);
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
@@ -767,7 +767,8 @@ where
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
+ {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
@@ -1119,8 +1120,8 @@ where
}
fn metaplex_instruction_materializations(
- rows: std::vec::Vec,
-) -> std::vec::Vec {
+ rows: std::vec::Vec,
+) -> std::vec::Vec {
let materializer_name = ks_lib::MtApiEventMaterializer::identity(
&ks_lib::MtMetadataMetaplexTokenMetadataMaterializer,
)
@@ -1613,8 +1614,8 @@ mod tests {
#[test]
fn materialization_evidence_is_owned_by_the_metaplex_materializer() {
- fn row(processor_name: &str) -> ks_store::MaterializedEventQueryRow {
- return ks_store::MaterializedEventQueryRow {
+ fn row(processor_name: &str) -> ks_store::MaterializedOutputQueryRow {
+ return ks_store::MaterializedOutputQueryRow {
processor_name: processor_name.to_string(),
processor_version: "0.4.8-pre.13".to_string(),
input_key: "input".to_string(),
diff --git a/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs b/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
index 9a172cd..0ee25de 100644
--- a/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
+++ b/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
-// version: 19
+// version: 20
//! Devnet ATA execution with stateful and canonical post-validation.
@@ -102,7 +102,7 @@ pub struct DevnetSplAssociatedTokenAccountExecutionSummary {
/// Second replay proving idempotence for the same decoder version and input.
pub idempotence_replay: std::option::Option,
/// Exact materialized rows produced for the submitted Token transaction.
- pub materializations: std::vec::Vec,
+ pub materializations: std::vec::Vec,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option,
}
@@ -424,7 +424,7 @@ where
};
diagnostic.decode_replayed = decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
@@ -433,7 +433,8 @@ where
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
+ {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!(
diff --git a/ks-pipeline-demo-scenarios/src/spl/memo.rs b/ks-pipeline-demo-scenarios/src/spl/memo.rs
index e93c06c..900c5d9 100644
--- a/ks-pipeline-demo-scenarios/src/spl/memo.rs
+++ b/ks-pipeline-demo-scenarios/src/spl/memo.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
-// version: 15
+// version: 16
//! Devnet SPL Memo v4 execution with canonical post-validation.
@@ -107,7 +107,7 @@ pub struct DevnetMemoExecutionSummary {
/// Second replay proving that the same decoder version and input are idempotent.
pub idempotence_replay: std::option::Option,
/// Exact persisted transaction annotation rows for the submitted signature.
- pub annotations: std::vec::Vec,
+ pub annotations: std::vec::Vec,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option,
}
@@ -490,7 +490,7 @@ where
};
diagnostic.decode_replayed = decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::Some("materializer.transaction.annotations".to_string()),
std::option::Option::Some("transaction_annotation".to_string()),
std::option::Option::Some(signature.0.clone()),
@@ -500,7 +500,7 @@ where
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
summary.annotations =
- match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic.diagnostics.push(format!("Memo annotation query failed: {error}"));
diff --git a/ks-pipeline-demo-scenarios/src/spl/token/execution.rs b/ks-pipeline-demo-scenarios/src/spl/token/execution.rs
index e1617f7..345c29b 100644
--- a/ks-pipeline-demo-scenarios/src/spl/token/execution.rs
+++ b/ks-pipeline-demo-scenarios/src/spl/token/execution.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs
-// version: 19
+// version: 20
//! Devnet classic SPL Token execution with stateful and canonical post-validation.
@@ -99,7 +99,7 @@ pub struct DevnetSplTokenExecutionSummary {
/// Second replay proving idempotence for the same decoder version and input.
pub idempotence_replay: std::option::Option,
/// Exact materialized rows produced for the submitted Token transaction.
- pub materializations: std::vec::Vec,
+ pub materializations: std::vec::Vec,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option,
}
@@ -390,7 +390,7 @@ where
};
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
@@ -399,7 +399,8 @@ where
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
+ {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
diff --git a/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs b/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
index 5cd16c5..2e5a4f3 100644
--- a/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
+++ b/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
-// version: 18
+// version: 19
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
@@ -662,7 +662,7 @@ where
"lifecycle predecessor decode and materialization did not complete",
));
}
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
@@ -671,7 +671,8 @@ where
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
+ {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
diff --git a/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs b/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
index a81ed5a..0fbd42a 100644
--- a/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
+++ b/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
-// version: 12
+// version: 13
//! Devnet Token-2022 execution with stateful and canonical post-validation.
@@ -99,7 +99,7 @@ pub struct DevnetSplToken2022ExecutionSummary {
/// Second replay proving idempotence for the same decoder version and input.
pub idempotence_replay: std::option::Option,
/// Exact materialized rows produced for the submitted Token transaction.
- pub materializations: std::vec::Vec,
+ pub materializations: std::vec::Vec,
/// Aggregated post-execution validation diagnostic.
pub post_execution: std::option::Option,
}
@@ -390,7 +390,7 @@ where
};
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
summary.decode_replay = std::option::Option::Some(first_replay);
- let filter = match ks_store::MaterializedEventFilter::new(
+ let filter = match ks_store::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::Some(signature.0.clone()),
@@ -399,7 +399,8 @@ where
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
+ let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
+ {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
diagnostic
diff --git a/ks-pipeline/CHANGELOG.md b/ks-pipeline/CHANGELOG.md
index 92f9acc..b06f5a8 100644
--- a/ks-pipeline/CHANGELOG.md
+++ b/ks-pipeline/CHANGELOG.md
@@ -1,8 +1,16 @@
-
+
# CHANGELOG — ks-pipeline
+## `0.5.3-pre.003`
+
+- corrige le premier delta `pre.003` en utilisant le nom canonique exporté `MdCanonicalInnerInstructionGroup` pour la reconstruction des chemins CPI.
+- aligne Core extraction sur le nouveau baseline `ks-store` : conservation de `block_time`, `stack_height`, `returnData` et persistance du contexte N2 nécessaire au replay ;
+- reconstruit les chemins CPI imbriqués à partir de `stackHeight` lorsque l'information est disponible et conserve un fallback déterministe vers la top-level lorsque Solana ne fournit pas cette hauteur ;
+- enrichit les inputs N2 -> N3 avec le contexte Core v3 sans modifier le dispatch fonctionnel des décodeurs actuels ;
+- adapte les lectures de matérialisation au contrat `MaterializedOutput*` renommé avec `k_sol_mat_outputs`.
+
## `0.5.1-pre.006`
- déplace ses fixtures de configuration vers le contrat runtime résolu sous `test-fixtures/config/` ;
diff --git a/ks-pipeline/src/backfill.rs b/ks-pipeline/src/backfill.rs
index 5ed06ed..533d3aa 100644
--- a/ks-pipeline/src/backfill.rs
+++ b/ks-pipeline/src/backfill.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline/src/backfill.rs
-// version: 11
+// version: 12
//! Reusable HTTP transaction backfill orchestration.
@@ -1409,7 +1409,7 @@ where
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
let observation = match payload_result {
std::result::Result::Ok(value) => {
- value.with_status(ks_store::TransactionObservationStatus::Persisted)
+ value.with_status(ks_store::AcquisitionObservationStatus::Persisted)
},
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
@@ -1456,7 +1456,7 @@ where
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
let observation = match payload_result {
std::result::Result::Ok(value) => {
- value.with_status(ks_store::TransactionObservationStatus::Missing)
+ value.with_status(ks_store::AcquisitionObservationStatus::Missing)
},
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
@@ -1538,7 +1538,7 @@ fn base_observation(
client.provider().to_string(),
"solana_http_json_rpc".to_string(),
"getTransaction".to_string(),
- ks_store::TransactionObservationOrigin::Backfill,
+ ks_store::AcquisitionObservationOrigin::Backfill,
received_at,
);
let observation = match observation_result {
diff --git a/ks-pipeline/src/core_extraction.rs b/ks-pipeline/src/core_extraction.rs
index c8260ad..acffad7 100644
--- a/ks-pipeline/src/core_extraction.rs
+++ b/ks-pipeline/src/core_extraction.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline/src/core_extraction.rs
-// version: 11
+// version: 13
//! Canonical Solana transaction to normalized core extraction pipeline.
@@ -568,6 +568,11 @@ pub fn extract_raw_transaction_to_core(
"canonical transaction slot does not match raw row slot",
));
}
+ if transaction.block_time != row.block_time {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "canonical transaction block time does not match raw row block time",
+ ));
+ }
let computed_hash_result = transaction.canonical_json_hash();
let computed_hash = match computed_hash_result {
std::result::Result::Ok(value) => value,
@@ -594,6 +599,7 @@ pub fn extract_raw_transaction_to_core(
let core_transaction_result = ks_store::CoreTransactionInsert::new(
transaction.primary_signature.clone(),
transaction.slot,
+ transaction.block_time,
false,
std::option::Option::None,
);
@@ -620,6 +626,7 @@ pub fn extract_raw_transaction_to_core(
inner_instructions,
logs: std::vec::Vec::new(),
balance_changes: std::vec::Vec::new(),
+ return_data: std::option::Option::None,
});
}
@@ -633,6 +640,7 @@ fn build_bundle_with_metadata(
let core_transaction_result = ks_store::CoreTransactionInsert::new(
transaction.primary_signature.clone(),
transaction.slot,
+ transaction.block_time,
failed,
metadata.error.clone(),
);
@@ -660,6 +668,11 @@ fn build_bundle_with_metadata(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
+ let return_data_result = crate::core_extraction::extract_return_data(transaction, metadata);
+ let return_data = match return_data_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
return std::result::Result::Ok(ks_store::CoreExtractionBundle {
raw_transaction_id: row.id,
ledger_identity,
@@ -669,9 +682,32 @@ fn build_bundle_with_metadata(
inner_instructions,
logs,
balance_changes,
+ return_data,
});
}
+fn extract_return_data(
+ transaction: &ks_lib::MdCanonicalTransaction,
+ metadata: &ks_lib::MdCanonicalTransactionMetadata,
+) -> ks_core::Result> {
+ let value = match &metadata.return_data {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
+ };
+ let insert_result = ks_store::CoreReturnDataInsert::new(
+ transaction.primary_signature.clone(),
+ transaction.slot,
+ value.program_id.clone(),
+ value.data_base64.clone(),
+ );
+ return match insert_result {
+ std::result::Result::Ok(insert) => {
+ std::result::Result::Ok(std::option::Option::Some(insert))
+ },
+ std::result::Result::Err(error) => std::result::Result::Err(error),
+ };
+}
+
fn ledger_identity_from_raw(
row: &ks_store::RawTransactionRow,
) -> ks_core::Result {
@@ -819,6 +855,7 @@ fn extract_instructions(
transaction.slot,
path,
program_id,
+ instruction.stack_height,
accounts_json,
payload_json,
);
@@ -836,9 +873,12 @@ fn extract_instructions(
let mut inner_instructions = std::vec::Vec::new();
if let std::option::Option::Some(metadata) = &transaction.metadata {
for group in &metadata.inner_instructions {
- let parent_path = group.parent_instruction_index.to_string();
- for (inner_index, instruction) in group.instructions.iter().enumerate() {
- let path = format!("{parent_path}/{inner_index}");
+ let paths_result = crate::core_extraction::resolve_inner_instruction_paths(group);
+ let paths = match paths_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
+ for (instruction, path) in group.instructions.iter().zip(paths.iter()) {
let resolved_result =
crate::core_extraction::resolve_instruction(&keys, instruction);
let (program_id, accounts_json, payload_json, payload_hash) = match resolved_result
@@ -849,9 +889,10 @@ fn extract_instructions(
let insert_result = ks_store::CoreInnerInstructionInsert::new(
transaction.primary_signature.clone(),
transaction.slot,
- parent_path.clone(),
- path,
+ path.0.clone(),
+ path.1.clone(),
program_id,
+ instruction.stack_height,
accounts_json,
payload_json,
);
@@ -871,6 +912,57 @@ fn extract_instructions(
return std::result::Result::Ok((instructions, inner_instructions));
}
+fn resolve_inner_instruction_paths(
+ group: &ks_lib::MdCanonicalInnerInstructionGroup,
+) -> ks_core::Result> {
+ let root_path = group.parent_instruction_index.to_string();
+ let mut stack = std::vec::Vec::<(u32, std::string::String)>::new();
+ let mut child_counts = std::collections::BTreeMap::::new();
+ let mut output = std::vec::Vec::with_capacity(group.instructions.len());
+ for instruction in &group.instructions {
+ let parent_path = match instruction.stack_height {
+ std::option::Option::Some(height) if height <= 1 => {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "inner instruction stack height must be greater than one",
+ ));
+ },
+ std::option::Option::Some(2) => {
+ stack.clear();
+ root_path.clone()
+ },
+ std::option::Option::Some(height) => {
+ while stack.last().is_some_and(|entry| return entry.0 >= height) {
+ stack.pop();
+ }
+ match stack.last() {
+ std::option::Option::Some((parent_height, path))
+ if parent_height.saturating_add(1) == height =>
+ {
+ path.clone()
+ },
+ _ => {
+ return std::result::Result::Err(ks_core::Error::invalid_state(
+ "inner instruction stack height is inconsistent with invocation order",
+ ));
+ },
+ }
+ },
+ std::option::Option::None => {
+ stack.clear();
+ root_path.clone()
+ },
+ };
+ let child_index = child_counts.entry(parent_path.clone()).or_insert(0);
+ let instruction_path = format!("{parent_path}/{}", *child_index);
+ *child_index = child_index.saturating_add(1);
+ if let std::option::Option::Some(height) = instruction.stack_height {
+ stack.push((height, instruction_path.clone()));
+ }
+ output.push((parent_path, instruction_path));
+ }
+ return std::result::Result::Ok(output);
+}
+
fn resolve_instruction(
keys: &[std::string::String],
instruction: &ks_lib::MdCanonicalCompiledInstruction,
@@ -918,7 +1010,11 @@ fn extract_logs(
metadata: &ks_lib::MdCanonicalTransactionMetadata,
) -> ks_core::Result> {
let mut output = std::vec::Vec::with_capacity(metadata.log_messages.len());
- let links = crate::core_extraction::resolve_log_links(transaction, metadata);
+ let links_result = crate::core_extraction::resolve_log_links(transaction, metadata);
+ let links = match links_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
for (index, text) in metadata.log_messages.iter().enumerate() {
let log_index_result = u32::try_from(index);
let log_index = match log_index_result {
@@ -959,10 +1055,12 @@ fn extract_logs(
fn resolve_log_links(
transaction: &ks_lib::MdCanonicalTransaction,
metadata: &ks_lib::MdCanonicalTransactionMetadata,
-) -> std::vec::Vec<(
- std::option::Option,
- std::option::Option,
-)> {
+) -> ks_core::Result<
+ std::vec::Vec<(
+ std::option::Option,
+ std::option::Option,
+ )>,
+> {
let keys = crate::core_extraction::resolved_account_keys(transaction);
let mut top_level = std::vec::Vec::<(std::string::String, std::string::String)>::new();
for (index, instruction) in transaction.message.instructions.iter().enumerate() {
@@ -972,18 +1070,23 @@ fn resolve_log_links(
top_level.push((program_id.clone(), index.to_string()));
}
}
- let mut inner_by_parent = std::collections::BTreeMap::<
+ let mut inner_by_root = std::collections::BTreeMap::<
std::string::String,
- std::vec::Vec<(std::string::String, std::string::String)>,
+ std::vec::Vec<(std::string::String, std::string::String, std::option::Option)>,
>::new();
for group in &metadata.inner_instructions {
- let parent = group.parent_instruction_index.to_string();
- let entries = inner_by_parent.entry(parent.clone()).or_default();
- for (index, instruction) in group.instructions.iter().enumerate() {
+ let root = group.parent_instruction_index.to_string();
+ let paths_result = crate::core_extraction::resolve_inner_instruction_paths(group);
+ let paths = match paths_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
+ let entries = inner_by_root.entry(root).or_default();
+ for (instruction, path) in group.instructions.iter().zip(paths.iter()) {
if let std::option::Option::Some(program_id) =
keys.get(usize::from(instruction.program_id_index))
{
- entries.push((program_id.clone(), format!("{parent}/{index}")));
+ entries.push((program_id.clone(), path.1.clone(), instruction.stack_height));
}
}
}
@@ -1011,18 +1114,20 @@ fn resolve_log_links(
}
resolved
} else {
- let parent_path = stack.first().and_then(|entry| return entry.1.clone());
- match parent_path {
- std::option::Option::Some(parent) => {
- let cursor = inner_cursors.entry(parent.clone()).or_insert(0);
+ let root_path = stack.first().and_then(|entry| return entry.1.clone());
+ match root_path {
+ std::option::Option::Some(root) => {
+ let cursor = inner_cursors.entry(root.clone()).or_insert(0);
let mut resolved = std::option::Option::None;
if let std::option::Option::Some(entries) =
- inner_by_parent.get(parent.as_str())
+ inner_by_root.get(root.as_str())
{
while *cursor < entries.len() {
let candidate = &entries[*cursor];
*cursor += 1;
- if candidate.0 == program_id {
+ let depth_matches =
+ candidate.2.is_none_or(|height| return height == depth);
+ if candidate.0 == program_id && depth_matches {
resolved = std::option::Option::Some(candidate.1.clone());
break;
}
@@ -1067,7 +1172,7 @@ fn resolve_log_links(
},
}
}
- return output;
+ return std::result::Result::Ok(output);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -1570,6 +1675,7 @@ mod tests {
id: 1,
signature: transaction.primary_signature.clone(),
slot: 42,
+ block_time: transaction.block_time,
canonical_json: std::option::Option::Some(json),
canonical_json_hash: std::option::Option::Some(hash),
canonical_format_version: 1,
@@ -1598,6 +1704,105 @@ mod tests {
assert_eq!(bundle.logs[1].instruction_path.as_deref(), std::option::Option::Some("0/0"));
}
+ #[test]
+ fn nested_cpi_paths_preserve_immediate_parent_from_stack_height() {
+ let mut transaction = crate::core_extraction::tests::sample_transaction();
+ let metadata = match transaction.metadata.as_mut() {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => panic!("sample metadata is required"),
+ };
+ let template = metadata.inner_instructions[0].instructions[0].clone();
+ let mut direct = template.clone();
+ direct.stack_height = std::option::Option::Some(2);
+ let mut nested = template.clone();
+ nested.stack_height = std::option::Option::Some(3);
+ let mut sibling = template;
+ sibling.stack_height = std::option::Option::Some(2);
+ metadata.inner_instructions[0].instructions = std::vec![direct, nested, sibling];
+ let extraction_result = crate::core_extraction::extract_instructions(&transaction);
+ let (_top_level, inner) = match extraction_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => panic!("nested CPI extraction failed: {error}"),
+ };
+ assert_eq!(inner[0].parent_instruction_path, "0");
+ assert_eq!(inner[0].instruction_path, "0/0");
+ assert_eq!(inner[1].parent_instruction_path, "0/0");
+ assert_eq!(inner[1].instruction_path, "0/0/0");
+ assert_eq!(inner[2].parent_instruction_path, "0");
+ assert_eq!(inner[2].instruction_path, "0/1");
+ assert_eq!(inner[1].stack_height, std::option::Option::Some(3));
+ }
+
+ #[test]
+ fn inconsistent_cpi_stack_height_is_rejected() {
+ let mut transaction = crate::core_extraction::tests::sample_transaction();
+ let metadata = match transaction.metadata.as_mut() {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => panic!("sample metadata is required"),
+ };
+ let template = metadata.inner_instructions[0].instructions[0].clone();
+ let mut direct = template.clone();
+ direct.stack_height = std::option::Option::Some(2);
+ let mut skipped_parent = template;
+ skipped_parent.stack_height = std::option::Option::Some(4);
+ metadata.inner_instructions[0].instructions = std::vec![direct, skipped_parent];
+ let extraction_result = crate::core_extraction::extract_instructions(&transaction);
+ assert!(extraction_result.is_err());
+ }
+
+ #[test]
+ fn absent_cpi_stack_height_falls_back_to_top_level_parent() {
+ let mut transaction = crate::core_extraction::tests::sample_transaction();
+ let metadata = match transaction.metadata.as_mut() {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => panic!("sample metadata is required"),
+ };
+ metadata.inner_instructions[0].instructions[0].stack_height = std::option::Option::None;
+ let extraction_result = crate::core_extraction::extract_instructions(&transaction);
+ let (_top_level, inner) = match extraction_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => panic!("CPI extraction failed: {error}"),
+ };
+ assert_eq!(inner[0].parent_instruction_path, "0");
+ assert_eq!(inner[0].instruction_path, "0/0");
+ assert_eq!(inner[0].stack_height, std::option::Option::None);
+ }
+
+ #[test]
+ fn extraction_preserves_block_time_and_return_data() {
+ let mut transaction = crate::core_extraction::tests::sample_transaction();
+ let metadata = match transaction.metadata.as_mut() {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => panic!("sample metadata is required"),
+ };
+ metadata.return_data = std::option::Option::Some(ks_lib::MdCanonicalReturnData {
+ program_id: "11111111111111111111111111111111".to_string(),
+ data_base64: "AQID".to_string(),
+ });
+ let row = crate::core_extraction::tests::raw_row(&transaction);
+ let extraction_result = crate::extract_raw_transaction_to_core(&row);
+ let bundle = match extraction_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => panic!("Core extraction failed: {error}"),
+ };
+ assert_eq!(bundle.transaction.block_time, transaction.block_time);
+ let return_data = match bundle.return_data {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => panic!("return data must be preserved"),
+ };
+ assert_eq!(return_data.program_id, "11111111111111111111111111111111");
+ assert_eq!(return_data.data_base64, "AQID");
+ }
+
+ #[test]
+ fn extraction_rejects_block_time_mismatch() {
+ let transaction = crate::core_extraction::tests::sample_transaction();
+ let mut row = crate::core_extraction::tests::raw_row(&transaction);
+ row.block_time = std::option::Option::Some(1_700_000_001);
+ let result = crate::extract_raw_transaction_to_core(&row);
+ assert!(result.is_err());
+ }
+
#[test]
fn extraction_rejects_hash_mismatch() {
let transaction = crate::core_extraction::tests::sample_transaction();
diff --git a/ks-pipeline/src/decode_replay.rs b/ks-pipeline/src/decode_replay.rs
index c6c00d5..a6b4474 100644
--- a/ks-pipeline/src/decode_replay.rs
+++ b/ks-pipeline/src/decode_replay.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline/src/decode_replay.rs
-// version: 3
+// version: 4
//! Common contextual instruction decode and optional materialization pipeline.
@@ -1320,6 +1320,7 @@ fn observation_inserts(
proof_kind: proof_kind_code(observation.proof.kind).to_string(),
proof_json,
payload_json: observation.payload_json.clone(),
+ schema_provenance: std::option::Option::None,
transaction_failed: observation.transaction_failed,
transaction_error: observation.transaction_error.clone(),
observation_committed: observation.observation_committed,
@@ -1821,10 +1822,10 @@ mod tests {
clippy::implicit_return,
reason = "async_trait expansion triggers implicit_return on generated async trait methods."
)]
- async fn list_materialized_events(
+ async fn list_materialized_outputs(
&self,
- _filter: &ks_store::MaterializedEventFilter,
- ) -> ks_core::Result> {
+ _filter: &ks_store::MaterializedOutputFilter,
+ ) -> ks_core::Result> {
return std::result::Result::Ok(std::vec::Vec::new());
}
}
diff --git a/ks-pipeline/src/plan.rs b/ks-pipeline/src/plan.rs
index ae7fb47..50df693 100644
--- a/ks-pipeline/src/plan.rs
+++ b/ks-pipeline/src/plan.rs
@@ -1,5 +1,5 @@
// file: ks-pipeline/src/plan.rs
-// version: 3
+// version: 4
//! Pipeline planning primitives.
@@ -16,7 +16,7 @@ pub enum PipelineStage {
Decode,
/// Materialize business events.
Materialize,
- /// Aggregate materialized events.
+ /// Aggregate materialized outputs.
Aggregate,
/// Validate outputs.
Validate,
diff --git a/ks-store/CHANGELOG.md b/ks-store/CHANGELOG.md
index 80bcb2e..da9f34c 100644
--- a/ks-store/CHANGELOG.md
+++ b/ks-store/CHANGELOG.md
@@ -1,8 +1,53 @@
-
+
# CHANGELOG — ks-store
+## `0.5.3-pre.003`
+
+### Fix `pre.003-delta-fix-005`
+
+- remplace le passage des statements SQLx en `TRACE` par `ConnectOptions::disable_statement_logging`, afin que `ks-store` ne dépende pas directement de la crate `log` uniquement pour `LevelFilter` ;
+- conserve l'ouverture PostgreSQL via `PgConnectOptions` + `connect_with` et la journalisation fonctionnelle propre à `ks-store` sous `target: crate::TRACING_TARGET` ;
+- adapte le canari de connexion pour vérifier explicitement la désactivation du statement logging SQLx.
+
+
+### Fix `pre.003-delta-fix-004`
+
+- configure `PgConnectOptions` pour émettre les statements SQLx ordinaires au niveau `TRACE` avant l'ouverture du pool via `connect_with`, tout en conservant le target tiers `sqlx::query` ;
+- préserve les événements fonctionnels PostgreSQL `ks-store` sous `target: crate::TRACING_TARGET` avec `backend`, `domain` et `action` ;
+- ajoute un canari positif vérifiant le chemin `PgConnectOptions` + `TRACE` + `connect_with`.
+
+### Fix `pre.003-delta-fix-003`
+
+- corrige la branche `incomplete_signatures` de la sélection N2 -> N3 : le `SELECT` contextualisé projette désormais `stack_height` comme l'exige `CoreInstructionRow` ;
+- extrait cette requête rare dans `INCOMPLETE_DECODE_INPUTS_SQL` et ajoute un test contractuel vérifiant toutes les colonnes attendues par le mapper Core, afin d'éviter une nouvelle dérive requête/DTO.
+
+
+### Baseline N1-N3 PostgreSQL
+
+- corrige le premier delta `pre.003` : fermeture de l'`impl PostgresStore`, appels des helpers privés de tests via `super::` et retrait des validateurs de noms de tables du runtime/re-export crate-root ; ces validateurs restent des canaris privés `#[cfg(test)]`.
+- reconstruit directement le schéma actif sous `k_sol_*` avec un baseline candidat au gel de **16 tables** : les 13 tables historiques conservées plus `k_sol_obs_account_observations`, `k_sol_core_account_states` et `k_sol_core_return_data` révélées par le canari future-decoder ;
+- ajoute `block_time` aux ancres raw/Core transaction, `stack_height` aux contrats top-level/CPI et un lifecycle de processing symétrique pour les CPI ; la reconstruction de Core préserve le parent CPI immédiat lorsque la pile est disponible ;
+- renomme le journal N3 en `k_sol_mat_outputs` et aligne l'API publique sur `MaterializedOutputFilter`, `MaterializedOutputQueryRow` et `list_materialized_outputs` ;
+- réserve sur `k_sol_decode_events` une provenance nullable de schéma externe (`kind/id/version/hash`) afin qu'un futur décodeur IDL puisse tracer son schéma sans remodeler N1/N2/N3.
+
+### Ressources et initialisation
+
+- corrige `pre.003-delta-fix-002` l'ordre transactionnel des contraintes : les clés primaires référencées sont désormais appliquées avant les clés étrangères ; ajoute un canari couvrant les neuf dépendances FK du baseline afin d'empêcher un nouvel ordre invalide et le rollback complet observé sur base vide ;
+- remplace les migrations historiques `0001` à `0004` et les deux scripts de maintenance monolithiques par **236 ressources PostgreSQL atomiques** sous `migrations/postgres/` : tables, contraintes, index, `TRUNCATE` et `DROP`, une instruction SQL par fichier embarquée par `include_str!` ;
+- centralise l'initialisation dans un seul orchestrateur PostgreSQL transactionnel avec advisory lock ; les initialiseurs raw/Core/decode partiels sont supprimés, y compris dans les tests ;
+- refuse explicitement la présence d'objets historiques `kb_sol_*` au lieu de mélanger les deux namespaces ;
+- remplace la lecture `_sqlx_migrations` comme preuve du schéma courant par la vérification du contrat réellement observé ;
+- étend le résumé backend-agnostique aux catégories physiques `table` et `index`, avec **75 index attendus** sans exposer leurs noms.
+
+### Readiness et contrats
+
+- conserve explicitement la `returnData` en N2 et enrichit le replay Core v3 avec `block_time`, `stack_height`, parent CPI, contexte top-level et return data ;
+- introduit les DTO N1/N2 génériques d'observation/état de compte nécessaires au futur décodage de layouts inconnus, sans ajouter de structure Anchor/IDL spécifique ; leur ingestion/replay fonctionnel est raccordé dans la tranche repositories/replay ;
+- ajoute un canari workspace imposant `crate::Type` pour les types publics de `ks-store` utilisés dans leur crate propriétaire ;
+- ne crée aucune table DEX/trading ni projection metadata N4 et ne déclare pas encore le gel formel : le snapshot final reste prévu après les validations techniques.
+
## `0.5.3-pre.002-delta-fix-003`
- corrige deux appels de helpers strictement privés PostgreSQL qui avaient été qualifiés à tort via `crate::`; ils restent locaux à leur module et ne sont pas réexportés au crate-root ;
diff --git a/ks-store/Cargo.toml b/ks-store/Cargo.toml
index fcc1ad6..01787d2 100644
--- a/ks-store/Cargo.toml
+++ b/ks-store/Cargo.toml
@@ -1,5 +1,5 @@
# file: ks-store/Cargo.toml
-# version: 3
+# version: 5
[package]
name = "ks-store"
diff --git a/ks-store/README.md b/ks-store/README.md
index 5c4b20f..d74bf46 100644
--- a/ks-store/README.md
+++ b/ks-store/README.md
@@ -1,5 +1,5 @@
-
+
# ks-store
@@ -10,7 +10,7 @@
La crate expose :
- `Store` et `StoreOpenOptions` comme façade d'ouverture et handle persistant backend-agnostique ;
-- les DTO et entités raw, observations, Core, décodage, matérialisation et ledger ;
+- les DTO et entités N1 raw/acquisition, N2 Core, N3 decode/materialization et ledger ;
- les traits async `StoreHealthStore`, `RawTransactionStore`, `CoreTransactionStore`, `CoreExtractionStore` et `DecodePipelineStore` ;
- les filtres et résultats de replay sans préfixe backend ;
- des diagnostics et résumés runtime/init sanitisés ne révélant ni secret ni nom physique d'objet ;
@@ -26,11 +26,35 @@ La crate expose :
`StoreOpenOptions` ne dérive pas `Debug` et ne rend pas les options backend accessibles après construction. Les résumés publics exposent uniquement des informations explicitement sanitisées : code backend, présence d'une connexion, état d'initialisation, health, modèle logique et compteurs.
-Le backend PostgreSQL masque le descripteur de connexion et utilise l’unique target de crate `ks-store`, complété par `backend="postgres"`, `domain="ks-store.pg"` et un champ `action` structuré. Aucun DSN, password ou valeur bindée n'est destiné aux diagnostics publics.
+Le backend PostgreSQL masque le descripteur de connexion et utilise l'unique target de crate `ks-store`, complété par `backend="postgres"`, `domain="ks-store.pg"` et un champ `action` structuré. Aucun DSN, password ou valeur bindée n'est destiné aux diagnostics publics.
-## Schéma pendant `0.5.3`
+## Baseline PostgreSQL `0.5.3-pre.003`
-`0.5.3-pre.002` ne modifie pas encore les tables héritées `kb_sol_*`. Leur reconstruction sous `k_sol_*`, les ressources SQL atomiques et le gel N1/N2/N3 commencent en `pre.003` conformément au plan actif.
+`pre.003` reconstruit directement le schéma actif sous le namespace `k_sol_*`. Les quatre migrations historiques `0001` à `0004` et les deux scripts de maintenance monolithiques ne font plus partie de la chaîne active.
+
+Le baseline candidat au gel comprend **16 tables N1-N3** :
+
+- N1 : transactions raw, observations d'acquisition de transactions et observations génériques de comptes ;
+- N2 : transaction Core, clés de comptes, instructions top-level, CPI, logs, variations de balances, return data et états canoniques de comptes ;
+- N3/transverse : processing ledger, événements décodés, déclarations/observations de couverture et `k_sol_mat_outputs`.
+
+Les ressources PostgreSQL sont rangées sous `migrations/postgres/` et suivent la règle **une instruction SQL par fichier**. L'orchestrateur privé applique tables, contraintes et index dans une transaction unique et refuse explicitement un schéma historique contenant encore des objets `kb_sol_*`.
+
+Le baseline comporte actuellement **236 ressources SQL atomiques** et **75 index attendus**, comptés sans exposer leurs noms dans l'API publique.
+
+## Readiness des futurs décodeurs
+
+Le canari future-decoder a conduit à trois corrections génériques avant gel :
+
+- `block_time` devient une colonne de premier rang sur raw/Core transaction ;
+- `returnData` est conservée explicitement dans le Core via `k_sol_core_return_data` ;
+- un chemin générique d'observation/état de compte est réservé via `k_sol_obs_account_observations` et `k_sol_core_account_states`.
+
+Les instructions top-level et CPI restent physiquement séparées. Les deux contrats portent `stack_height` lorsque disponible ; les CPI conservent un `parent_instruction_path` vers le parent immédiat reconstructible.
+
+N3 réserve en outre une provenance de schéma nullable sur les observations décodées afin qu'un futur décodeur basé sur un schéma externe/IDL puisse tracer ce schéma sans remodeler le Core.
+
+Aucune structure `anchor_*`, DEX ou projection metadata N4 n'est créée en `pre.003`.
## Responsabilités
@@ -43,7 +67,7 @@ Le backend PostgreSQL masque le descripteur de connexion et utilise l’unique t
## Hors périmètre
-La crate ne décode pas les instructions, n'acquiert pas les transactions et ne décide pas quelle matérialisation ou projection N4 exécuter. Elle persiste les faits produits par `ks-lib` et orchestrés par `ks-pipeline`.
+La crate ne décode pas les instructions, n'acquiert pas elle-même les transactions/comptes et ne décide pas quelle matérialisation ou projection N4 exécuter. Elle persiste les faits produits par `ks-lib` et orchestrés par `ks-pipeline`.
## Relations
diff --git a/ks-store/TODO.md b/ks-store/TODO.md
index 01c62ed..8285e90 100644
--- a/ks-store/TODO.md
+++ b/ks-store/TODO.md
@@ -1,17 +1,19 @@
-
+
# TODO — ks-store
## Série `0.5.x`
-- [ ] `0.5.3` — reconstruire `kb_sol_* -> k_sol_*`, ajouter `block_time` aux ancres raw/Core, conserver top-level/CPI dans deux tables Core avec lifecycle/replay équivalents, `stack_height`/parent CPI audités et renommer le journal N3 en `k_sol_mat_outputs`.
-- [ ] `0.5.3` — déplacer les ressources sous `migrations/postgres/`, imposer une instruction SQL par fichier avec exécution privée via `include_str!`, séparer tables/keys/index/drop/truncate et supprimer le DDL dupliqué en Rust.
-- [ ] `0.5.3` — corriger l'invalidation descendante, formaliser les replays N1 -> N2 / N2 -> N3 / frontière N3 -> N4, les paginations/limites, les conversions BIGINT et les index justifiés par les requêtes réelles.
-- [ ] `0.5.3` — finaliser en `pre.005` la présentation fonctionnelle des surfaces `demo_store_*`, `demo_config` et le splash autour des modèles/compteurs du nouveau baseline sans noms d'objets physiques ; le renommage technique `demo_sql_* -> demo_store_*` est réalisé en `pre.002`.
-- [ ] `0.5.3` — exécuter avant gel le canari future-decoder/Anchor-readiness sur N1/N2/N3 : bytes d'instruction, comptes ordonnés/flags, hiérarchie CPI, logs, `returnData`, observations/états de comptes et provenance decoder/schema ; ajouter seulement les contrats Solana génériques manquants, sans table `anchor_*`.
-- [ ] `0.5.3` — produire le snapshot machine-readable des tables N1/N2/N3 réellement stabilisées après ce canari (13 tables existantes de baseline, plus éventuels contrats génériques nécessaires), documenter la politique N4 plus souple et les futures projections metadata asset/token + program metadata, puis ajouter les canaris anti-`kb_sol_*`, anti-backend leak, anti-secret et logging sous le target canonique `ks-store` avec domaine structuré `ks-store.pg`.
-- [ ] PostgreSQL - auditer le dimensionnement du pool selon les charges réelles.
-- [ ] Résilience - décider et implémenter, si justifié, un retry borné pour les timeouts transitoires.
-- [ ] Administration - définir les outils supplémentaires prévus par le ROADMAP avant leur implémentation.
-- [ ] Historique - documenter et tester les futures opérations historiques ajoutées à la crate.
+- [ ] `0.5.3` — finaliser en `pre.004` les repositories/replay au-dessus du baseline de 16 tables : sélection autonome des CPI, lecture logique top-level+CPI, invalidation descendante, replay N1 -> N2 / N2 -> N3 / frontière N3 -> N4 et ingestion/replay du nouveau couple observation/état de compte.
+- [ ] `0.5.3` — finaliser en `pre.004` pagination/limites, conversions BIGINT et plans/index justifiés par les requêtes réelles ; les 75 index du baseline `pre.003` restent optimisables avant gel.
+- [ ] `0.5.3` — finaliser en `pre.005` la présentation fonctionnelle des surfaces `demo_store_*`, `demo_config` et le splash autour des modèles/compteurs du nouveau baseline sans noms d'objets physiques.
+- [ ] `0.5.3` — produire le snapshot machine-readable du baseline N1/N2/N3 réellement stabilisé, ajouter les canaris de gel/anti-`kb_sol_*`/anti-backend leak/anti-secret, confirmer le canari future-decoder sur transactions + comptes et documenter la politique N4 plus souple.
+- [ ] `0.5.3` — valider PostgreSQL réel sur base vide/recréée : 16 tables, index attendus, réouverture idempotente, refus de l'ancien namespace, rollback et roundtrips raw/Core/decode/materialization.
+- [ ] `0.5.x` — concevoir les projections N4 metadata : asset/token metadata commune Metaplex + Token-2022 et program metadata séparée pour SPM, toutes deux reconstructibles depuis `k_sol_mat_outputs`.
+
+## Plus tard
+
+- [ ] définir une stratégie de pool/réessai configurable commune aux futurs backends sans exposer leurs types de connexion ;
+- [ ] décider si un journal append-only des tentatives de processing doit compléter le ledger courant ;
+- [ ] ajouter des projections spécialisées uniquement lorsqu'un décodeur/materializer et un besoin queryable réel en démontrent les invariants.
diff --git a/ks-store/USAGE.md b/ks-store/USAGE.md
index 5a7cf72..5700bbd 100644
--- a/ks-store/USAGE.md
+++ b/ks-store/USAGE.md
@@ -1,5 +1,5 @@
-
+
# Utilisation de ks-store
@@ -27,7 +27,30 @@ let store = match ks_store::Store::open(options).await {
};
```
-`Store::open` interprète les options du backend actif, crée la connexion, exécute l'auto-initialisation lorsqu'elle est configurée et capture un résumé d'initialisation sanitisé. PostgreSQL reste le seul backend opérationnel exigé en `0.5.3-pre.002`; un code backend inconnu est refusé proprement.
+`Store::open` interprète les options du backend actif, crée la connexion, exécute l'auto-initialisation lorsqu'elle est configurée et capture un résumé d'initialisation sanitisé. PostgreSQL reste le seul backend opérationnel exigé en `0.5.3`; un code backend inconnu est refusé proprement.
+
+Avec PostgreSQL, l'auto-initialisation utilise un **seul orchestrateur transactionnel global**. Il ne faut pas initialiser séparément raw/Core/decode : la cohérence du baseline est vérifiée comme un contrat unique.
+
+## Baseline PostgreSQL actif
+
+`0.5.3-pre.003` utilise directement le namespace `k_sol_*` et un baseline candidat au gel de 16 tables N1-N3. Une base contenant encore des objets `kb_sol_*` est refusée ; elle doit être supprimée/reconstruite explicitement.
+
+Les ressources actives sont sous :
+
+```text
+ks-store/migrations/postgres/
+ schema/
+ tables/
+ constraints/
+ indexes/
+ maintenance/
+ truncate/
+ drop/
+```
+
+Chaque fichier contient une seule instruction SQL. Les fonctions privées du backend l'embarquent par `include_str!`; le DDL n'est pas dupliqué dans des chaînes Rust.
+
+Le baseline courant compte 236 ressources SQL atomiques et 75 index attendus.
## Résumés sûrs
@@ -42,7 +65,7 @@ let runtime = match store.runtime_summary().await {
Ces contrats ne contiennent pas les options backend brutes. Le descripteur de connexion éventuellement exposé par `runtime.backend` est masqué par le backend propriétaire.
-Le résumé d'initialisation classe les ressources par modèles logiques et fournit des compteurs. Il expose aussi `objects`, une liste de catégories physiques sans noms d'objets : PostgreSQL publie déjà le total `table`, tandis que `pre.003` ajoutera le total `index` avec le nouveau baseline. Les noms physiques de tables, index et contraintes restent internes au backend.
+Le résumé d'initialisation classe les ressources par modèles logiques et fournit des compteurs. `objects` expose des catégories physiques sans noms d'objets. PostgreSQL publie actuellement les catégories `table` et `index`; un futur backend peut publier d'autres catégories sans modifier le contrat desktop.
## Diagnostics de ressources
@@ -61,7 +84,7 @@ for resource in raw_resources {
}
```
-Les méthodes `core_resource_diagnostics`, `decode_resource_diagnostics` et `known_resource_diagnostics` suivent le même contrat backend-agnostique.
+Les méthodes `core_resource_diagnostics`, `decode_resource_diagnostics` et `known_resource_diagnostics` suivent le même contrat backend-agnostique. Les codes de ressources publics sont logiques ; les noms physiques PostgreSQL restent internes.
## Contrats de repositories
@@ -73,7 +96,33 @@ Les traits publics effectivement implémentés sont :
- `CoreExtractionStore` ;
- `DecodePipelineStore`.
-`Store` implémente ces traits et délègue au backend privé. Les anciens traits historiques sans implémentation réelle ont été retirés avant gel de l'API.
+`Store` implémente ces traits et délègue au backend privé.
+
+Le schéma `pre.003` réserve aussi les contrats génériques d'observation/état de comptes nécessaires au gel N1/N2. Leur ingestion/replay opérationnel est raccordé dans la tranche repositories/replay suivante ; leur présence dans le baseline évite une modification structurelle ultérieure pour un futur décodeur de comptes.
+
+## Core et replay N2 -> N3
+
+Le replay d'instruction Core transporte désormais :
+
+- `block_time` nullable ;
+- `stack_height` nullable ;
+- `parent_instruction_path` pour les CPI ;
+- le contexte ordonné des instructions top-level ;
+- la `returnData` transactionnelle lorsqu'elle existe.
+
+Le contrat de replay Core est en version 3. Les anciens constructeurs restent utilisables pour les tests/sources synthétiques ; `ks-store` enrichit les inputs issus du Core persistant avec le contexte disponible.
+
+Les instructions top-level et CPI restent deux tables physiques. La sélection logique commune et le replay autonome des CPI sont finalisés dans la tranche repositories/replay.
+
+## Matérialisation N3
+
+Le journal générique est `k_sol_mat_outputs`. L'API publique utilise désormais :
+
+- `MaterializedOutputFilter` ;
+- `MaterializedOutputQueryRow` ;
+- `DecodePipelineStore::list_materialized_outputs`.
+
+`k_sol_mat_outputs` reste obligatoire même lorsque des projections N4 spécialisées existeront.
## Replay et requêtes bornées
@@ -92,7 +141,7 @@ let entities = match store.replay_entity_summaries(entity_filter).await {
};
```
-Les contrats publics utilisent `ReplayTransaction*`, `ReplayProgram*` et `ReplayEntity*`. Le scope d'instruction utilise désormais `TopLevel`, `Inner` et `Logs`; le terme historique `outer` n'est pas introduit dans la nouvelle API.
+Les contrats publics utilisent `ReplayTransaction*`, `ReplayProgram*` et `ReplayEntity*`. Le scope d'instruction utilise `TopLevel`, `Inner` et `Logs`; le terme historique `outer` n'est pas introduit dans la nouvelle API store.
## Pagination bornée
@@ -106,7 +155,7 @@ assert_eq!(first_page.limit, ks_store::DEFAULT_PAGE_SIZE);
assert!(next_page.limit <= ks_store::MAX_PAGE_SIZE);
```
-La refonte cursorisée prévue par le plan `0.5.3` reste dans la tranche repositories/replay ultérieure.
+La refonte cursorisée et la suppression des gros offsets comme contrat durable appartiennent à la tranche repositories/replay.
## Transactions atomiques
@@ -116,9 +165,20 @@ Les bundles `CoreExtractionBundle`, `DecodePersistenceBundle` et `Materializatio
Les tests opt-in utilisent `KS_SECRET_POSTGRES_TEST_URL`. Aucun credential réel ne doit être enregistré dans les fixtures ou logs. Les erreurs d'ouverture de connexion sont volontairement sanitisées.
-## Limites de `pre.002`
+Pour `pre.003`, les validations PostgreSQL à exécuter après compilation sont particulièrement importantes :
-- les tables restent temporairement sous leur namespace historique jusqu'à `pre.003` ;
-- les migrations PostgreSQL ne sont pas encore déplacées sous `migrations/postgres/` ;
-- la pagination cursorisée et l'invalidation descendante sont traitées dans la tranche repositories/replay ;
-- la refonte complète des fenêtres desktop `demo_store_*` est prévue dans la tranche consommateurs.
+- init sur base vide ;
+- réouverture idempotente ;
+- présence des 16 tables et 75 index attendus ;
+- refus d'un schéma historique `kb_sol_*` ;
+- roundtrip raw/Core/decode/materialization ;
+- rollback transactionnel Core/decode.
+
+## Travaux encore ouverts après `pre.003`
+
+- sélection/replay autonome des CPI et lecture logique top-level+CPI ;
+- invalidation descendante N1 -> N2 -> N3 -> N4 ;
+- replay opérationnel du nouveau flux d'observations/états de comptes ;
+- pagination cursorisée et revue finale des index par plans réels ;
+- snapshot machine-readable et gel formel en tranche de réconciliation technique ;
+- refonte desktop fonctionnelle finale autour du nouveau baseline.
diff --git a/ks-store/maintenance/drop_raw_core_store.sql b/ks-store/maintenance/drop_raw_core_store.sql
deleted file mode 100644
index 454f857..0000000
--- a/ks-store/maintenance/drop_raw_core_store.sql
+++ /dev/null
@@ -1,21 +0,0 @@
--- file: ks-store/maintenance/drop_raw_core_store.sql
--- version: 2
-
--- Drop raw and core Solana storage tables in reverse dependency order.
--- This script is intended for local development and test databases only.
--- It does not drop unrelated schemas, migrations, roles or configuration state.
--- It intentionally avoids CASCADE so unexpected dependencies fail loudly.
--- No custom PostgreSQL enum/domain types are owned by these 0.2.3/0.2.4 tables.
-
-BEGIN;
-
-DROP TABLE IF EXISTS kb_sol_core_balance_changes;
-DROP TABLE IF EXISTS kb_sol_core_logs;
-DROP TABLE IF EXISTS kb_sol_core_inner_instructions;
-DROP TABLE IF EXISTS kb_sol_core_instructions;
-DROP TABLE IF EXISTS kb_sol_core_account_keys;
-DROP TABLE IF EXISTS kb_sol_core_transactions;
-DROP TABLE IF EXISTS kb_sol_obs_transaction_observations;
-DROP TABLE IF EXISTS kb_sol_raw_transactions;
-
-COMMIT;
diff --git a/ks-store/maintenance/reset_derived_store_keep_raw.sql b/ks-store/maintenance/reset_derived_store_keep_raw.sql
deleted file mode 100644
index 1a7d4f5..0000000
--- a/ks-store/maintenance/reset_derived_store_keep_raw.sql
+++ /dev/null
@@ -1,30 +0,0 @@
--- file: ks-store/maintenance/reset_derived_store_keep_raw.sql
--- version: 3
-
--- Reset every reconstructible Solana projection while preserving acquisition data.
--- Intended for controlled local/research rebuilds only.
--- Preserved tables:
--- - kb_sol_raw_transactions
--- - kb_sol_obs_transaction_observations
--- No CASCADE is used: unexpected dependencies fail loudly.
-
-BEGIN;
-TRUNCATE TABLE
- kb_sol_mat_events,
- kb_sol_decode_coverage_observations,
- kb_sol_decode_coverage_declarations,
- kb_sol_decode_events,
- kb_sol_ops_processing_ledger,
- kb_sol_core_balance_changes,
- kb_sol_core_logs,
- kb_sol_core_inner_instructions,
- kb_sol_core_instructions,
- kb_sol_core_account_keys,
- kb_sol_core_transactions
-RESTART IDENTITY;
-UPDATE kb_sol_raw_transactions
-SET
- processing_state = 'received',
- lifecycle_reason = NULL,
- updated_at = NOW();
-COMMIT;
diff --git a/ks-store/migrations/0001_canonical_transaction_store.sql b/ks-store/migrations/0001_canonical_transaction_store.sql
deleted file mode 100644
index e7a7ea1..0000000
--- a/ks-store/migrations/0001_canonical_transaction_store.sql
+++ /dev/null
@@ -1,94 +0,0 @@
--- file: ks-store/migrations/0001_canonical_transaction_store.sql
--- version: 2
-
--- Current canonical transaction and acquisition observation store.
--- This baseline contains only active tables and uses the current PostgreSQL schema.
--- Historical 0.2.x upgrade logic was validated before this baseline was consolidated.
-
-CREATE TABLE IF NOT EXISTS kb_sol_raw_transactions (
- id BIGSERIAL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- canonical_json JSONB,
- canonical_json_hash TEXT,
- canonical_format_version INTEGER NOT NULL DEFAULT 1,
- retention_state TEXT NOT NULL DEFAULT 'full',
- processing_state TEXT NOT NULL DEFAULT 'received',
- lifecycle_reason TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_raw_transactions PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_raw_transactions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0),
- CONSTRAINT ck_kb_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged')),
- CONSTRAINT ck_kb_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed')),
- CONSTRAINT ck_kb_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL)
- );
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_raw_transactions_signature ON kb_sol_raw_transactions (signature);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_slot ON kb_sol_raw_transactions (slot);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_created_at ON kb_sol_raw_transactions (created_at);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_processing ON kb_sol_raw_transactions (processing_state);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_canonical_hash ON kb_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL;
-
-CREATE TABLE IF NOT EXISTS kb_sol_obs_transaction_observations (
- id BIGSERIAL,
- raw_transaction_id BIGINT,
- observation_key TEXT NOT NULL,
- signature TEXT,
- slot BIGINT,
- provider TEXT NOT NULL,
- endpoint_code TEXT,
- protocol TEXT NOT NULL,
- acquisition_method TEXT NOT NULL,
- origin TEXT NOT NULL,
- commitment TEXT,
- capture_session_id TEXT,
- filter_code TEXT,
- detected_at TIMESTAMPTZ,
- received_at TIMESTAMPTZ NOT NULL,
- normalized_at TIMESTAMPTZ,
- persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- payload_size_bytes BIGINT,
- source_payload_hash TEXT,
- status TEXT NOT NULL DEFAULT 'received',
- error_code TEXT,
- error_message TEXT,
- CONSTRAINT pk_kb_sol_obs_transaction_observations PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
- CONSTRAINT ck_kb_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration')),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing')),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
- );
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_obs_transaction_observations_key ON kb_sol_obs_transaction_observations (observation_key);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_signature ON kb_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL;
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_slot ON kb_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL;
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_provider_method ON kb_sol_obs_transaction_observations (provider, acquisition_method);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_received_at ON kb_sol_obs_transaction_observations (received_at);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_raw_transaction ON kb_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL;
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_status ON kb_sol_obs_transaction_observations (status);
diff --git a/ks-store/migrations/0002_core_store.sql b/ks-store/migrations/0002_core_store.sql
deleted file mode 100644
index e6ccad1..0000000
--- a/ks-store/migrations/0002_core_store.sql
+++ /dev/null
@@ -1,196 +0,0 @@
--- file: ks-store/migrations/0002_core_store.sql
--- version: 2
-
--- Current normalized Solana core store.
--- This migration uses the current PostgreSQL schema from the active profile.
--- It must not create application schemas such as raw, core, obs, decode, mat or catalog.
--- Object naming convention: pk_ for primary keys, fk_ for foreign keys, ux_ for unique indexes and ix_ for non-unique indexes.
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_transactions (
- id BIGSERIAL,
- raw_transaction_id BIGINT,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- failed BOOLEAN NOT NULL DEFAULT FALSE,
- err_json JSONB,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_transactions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
- CONSTRAINT ck_kb_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_transactions_slot_non_negative CHECK (slot >= 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_transactions_signature
- ON kb_sol_core_transactions (signature);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_slot
- ON kb_sol_core_transactions (slot);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_created_at
- ON kb_sol_core_transactions (created_at);
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_account_keys (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- account_index INTEGER NOT NULL,
- account_key TEXT NOT NULL,
- source TEXT NOT NULL,
- writable BOOLEAN NOT NULL,
- signer BOOLEAN NOT NULL,
- executable BOOLEAN,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_account_keys PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_account_keys_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_account_keys_index_non_negative CHECK (account_index >= 0),
- CONSTRAINT ck_kb_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0),
- CONSTRAINT ck_kb_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly'))
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_account_keys_sig_index
- ON kb_sol_core_account_keys (signature, account_index);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_account_keys_key
- ON kb_sol_core_account_keys (account_key);
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_instructions (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- accounts_json JSONB NOT NULL,
- payload_json JSONB,
- payload_json_hash TEXT,
- processing_state TEXT NOT NULL DEFAULT 'pending',
- processor_name TEXT,
- processor_version TEXT,
- lifecycle_reason TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_instructions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'))
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_instructions_sig_path
- ON kb_sol_core_instructions (signature, instruction_path);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_program
- ON kb_sol_core_instructions (program_id);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_slot
- ON kb_sol_core_instructions (slot);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_processing
- ON kb_sol_core_instructions (processing_state);
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_inner_instructions (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- parent_instruction_path TEXT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- accounts_json JSONB NOT NULL,
- payload_json JSONB,
- payload_json_hash TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_inner_instructions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_inner_instructions_sig_path
- ON kb_sol_core_inner_instructions (signature, instruction_path);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_parent
- ON kb_sol_core_inner_instructions (signature, parent_instruction_path);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_program
- ON kb_sol_core_inner_instructions (program_id);
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_logs (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- log_index INTEGER NOT NULL,
- instruction_path TEXT,
- program_id TEXT,
- log_text TEXT,
- log_text_hash TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_logs PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_logs_index_non_negative CHECK (log_index >= 0),
- CONSTRAINT ck_kb_sol_core_logs_text_not_empty CHECK (log_text IS NULL OR length(btrim(log_text)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_logs_sig_index
- ON kb_sol_core_logs (signature, log_index);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_program
- ON kb_sol_core_logs (program_id)
- WHERE program_id IS NOT NULL;
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_path
- ON kb_sol_core_logs (signature, instruction_path)
- WHERE instruction_path IS NOT NULL;
-
-CREATE TABLE IF NOT EXISTS kb_sol_core_balance_changes (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- balance_change_index INTEGER NOT NULL,
- balance_kind TEXT NOT NULL,
- account_index INTEGER,
- account_key TEXT,
- mint TEXT,
- owner TEXT,
- pre_balance_json JSONB,
- post_balance_json JSONB,
- delta_json JSONB,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_balance_changes PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount')),
- CONSTRAINT ck_kb_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_balance_changes_sig_index
- ON kb_sol_core_balance_changes (signature, balance_change_index);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_account
- ON kb_sol_core_balance_changes (account_key)
- WHERE account_key IS NOT NULL;
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_mint
- ON kb_sol_core_balance_changes (mint)
- WHERE mint IS NOT NULL;
-
diff --git a/ks-store/migrations/0003_processing_ledger.sql b/ks-store/migrations/0003_processing_ledger.sql
deleted file mode 100644
index 52b5b01..0000000
--- a/ks-store/migrations/0003_processing_ledger.sql
+++ /dev/null
@@ -1,41 +0,0 @@
--- file: ks-store/migrations/0003_processing_ledger.sql
--- version: 2
-
--- Current processing ledger for canonical to core extraction and later pipeline stages.
--- This migration uses the current PostgreSQL schema from the active profile.
-
-CREATE TABLE IF NOT EXISTS kb_sol_ops_processing_ledger (
- id BIGSERIAL,
- stage TEXT NOT NULL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL,
- input_hash TEXT NOT NULL,
- status TEXT NOT NULL,
- attempt_count INTEGER NOT NULL DEFAULT 0,
- started_at TIMESTAMPTZ,
- finished_at TIMESTAMPTZ,
- error_code TEXT,
- error_message TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_ops_processing_ledger PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed')),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_ops_processing_ledger_identity
- ON kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_status
- ON kb_sol_ops_processing_ledger (stage, processor_name, status, updated_at);
-
-CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_input_hash
- ON kb_sol_ops_processing_ledger (input_hash);
diff --git a/ks-store/migrations/0004_decode_materialization_store.sql b/ks-store/migrations/0004_decode_materialization_store.sql
deleted file mode 100644
index e7d9359..0000000
--- a/ks-store/migrations/0004_decode_materialization_store.sql
+++ /dev/null
@@ -1,132 +0,0 @@
--- file: ks-store/migrations/0004_decode_materialization_store.sql
--- version: 2
-
--- Common versioned decode, coverage and materialization store.
--- This migration uses the current PostgreSQL schema from the active profile.
-
-CREATE TABLE IF NOT EXISTS kb_sol_decode_events (
- id BIGSERIAL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL,
- input_hash TEXT NOT NULL,
- event_key TEXT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- protocol_code TEXT NOT NULL,
- surface_code TEXT NOT NULL,
- event_code TEXT NOT NULL,
- event_name TEXT NOT NULL,
- event_family TEXT NOT NULL,
- source_kind TEXT NOT NULL,
- confidence TEXT NOT NULL,
- proof_kind TEXT NOT NULL,
- proof_jsonb JSONB NOT NULL,
- payload_jsonb JSONB NOT NULL,
- transaction_failed BOOLEAN NOT NULL,
- transaction_error_jsonb JSONB,
- observation_committed BOOLEAN NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_events PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_decode_events_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_events_processor_input_event
- ON kb_sol_decode_events (processor_name, processor_version, input_key, event_key);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_signature_path
- ON kb_sol_decode_events (signature, instruction_path);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_program_surface
- ON kb_sol_decode_events (program_id, surface_code, event_code);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_family_commit
- ON kb_sol_decode_events (event_family, transaction_failed, observation_committed);
-
-CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_declarations (
- id BIGSERIAL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- program_id TEXT NOT NULL,
- surface_code TEXT,
- entry_kind TEXT NOT NULL,
- entry_code TEXT NOT NULL,
- discriminator_hex TEXT,
- historical BOOLEAN NOT NULL DEFAULT FALSE,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_coverage_declarations PRIMARY KEY (id)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_declarations_identity
- ON kb_sol_decode_coverage_declarations (
- processor_name,
- processor_version,
- program_id,
- COALESCE(surface_code, ''),
- entry_kind,
- entry_code,
- COALESCE(discriminator_hex, '')
- );
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_declarations_program
- ON kb_sol_decode_coverage_declarations (program_id, processor_name, processor_version);
-
-CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_observations (
- id BIGSERIAL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL,
- input_hash TEXT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- surface_code TEXT,
- entry_code TEXT,
- discriminator_hex TEXT,
- status TEXT NOT NULL,
- recognized BOOLEAN NOT NULL,
- decoded_count INTEGER NOT NULL DEFAULT 0,
- materialized_count INTEGER NOT NULL DEFAULT 0,
- error_count INTEGER NOT NULL DEFAULT 0,
- transaction_failed BOOLEAN NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_coverage_observations PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_observations_identity
- ON kb_sol_decode_coverage_observations (processor_name, processor_version, input_key);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_program_status
- ON kb_sol_decode_coverage_observations (program_id, status, transaction_failed);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_entry
- ON kb_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code);
-
-CREATE TABLE IF NOT EXISTS kb_sol_mat_events (
- id BIGSERIAL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL,
- input_hash TEXT NOT NULL,
- output_key TEXT NOT NULL,
- source_event_key TEXT NOT NULL,
- source_decoder_name TEXT NOT NULL,
- source_decoder_version TEXT NOT NULL,
- source_decode_input_key TEXT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- materialized_family TEXT NOT NULL,
- payload_jsonb JSONB NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_mat_events PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_mat_events_slot_non_negative CHECK (slot >= 0)
-);
-
-CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_mat_events_processor_input_output
- ON kb_sol_mat_events (processor_name, processor_version, input_key, output_key);
-CREATE INDEX IF NOT EXISTS ix_kb_sol_mat_events_signature_family
- ON kb_sol_mat_events (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family);
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql
new file mode 100644
index 0000000..eca809a
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql
+-- version: 1
+
+-- Drop k_sol_core_account_keys explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_account_keys CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql
new file mode 100644
index 0000000..381d2c4
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql
+-- version: 1
+
+-- Drop k_sol_core_account_states explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_account_states CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql
new file mode 100644
index 0000000..225b34a
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql
+-- version: 1
+
+-- Drop k_sol_core_balance_changes explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_balance_changes CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql
new file mode 100644
index 0000000..b9f44b1
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql
+-- version: 1
+
+-- Drop k_sol_core_inner_instructions explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_inner_instructions CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql
new file mode 100644
index 0000000..e988284
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql
+-- version: 1
+
+-- Drop k_sol_core_instructions explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_instructions CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql
new file mode 100644
index 0000000..5dc2e59
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql
+-- version: 1
+
+-- Drop k_sol_core_logs explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_logs CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql
new file mode 100644
index 0000000..4a1c85f
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql
+-- version: 1
+
+-- Drop k_sol_core_return_data explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_return_data CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql
new file mode 100644
index 0000000..7aeebc5
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql
+-- version: 1
+
+-- Drop k_sol_core_transactions explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_core_transactions CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql
new file mode 100644
index 0000000..52ee97b
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql
+-- version: 1
+
+-- Drop k_sol_decode_coverage_declarations explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_decode_coverage_declarations CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql
new file mode 100644
index 0000000..7e7d92b
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql
+-- version: 1
+
+-- Drop k_sol_decode_coverage_observations explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_decode_coverage_observations CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql
new file mode 100644
index 0000000..d83ce74
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql
+-- version: 1
+
+-- Drop k_sol_decode_events explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_decode_events CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql
new file mode 100644
index 0000000..c89d857
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql
+-- version: 1
+
+-- Drop k_sol_mat_outputs explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_mat_outputs CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql
new file mode 100644
index 0000000..693a977
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql
+-- version: 1
+
+-- Drop k_sol_obs_account_observations explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_obs_account_observations CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql
new file mode 100644
index 0000000..690e6b7
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql
+-- version: 1
+
+-- Drop k_sol_obs_transaction_observations explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_obs_transaction_observations CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql
new file mode 100644
index 0000000..8c6ab7b
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql
+-- version: 1
+
+-- Drop k_sol_ops_processing_ledger explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_ops_processing_ledger CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql
new file mode 100644
index 0000000..81be279
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql
+-- version: 1
+
+-- Drop k_sol_raw_transactions explicitly for maintenance.
+DROP TABLE IF EXISTS k_sol_raw_transactions CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql
new file mode 100644
index 0000000..8b52918
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql
+-- version: 1
+
+-- Truncate k_sol_core_account_keys explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_account_keys RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql
new file mode 100644
index 0000000..f57e077
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql
+-- version: 1
+
+-- Truncate k_sol_core_account_states explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_account_states RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql
new file mode 100644
index 0000000..1e3c1f0
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql
+-- version: 1
+
+-- Truncate k_sol_core_balance_changes explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_balance_changes RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql
new file mode 100644
index 0000000..bce7f79
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql
+-- version: 1
+
+-- Truncate k_sol_core_inner_instructions explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_inner_instructions RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql
new file mode 100644
index 0000000..b99ac63
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql
+-- version: 1
+
+-- Truncate k_sol_core_instructions explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_instructions RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql
new file mode 100644
index 0000000..c8123b0
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql
+-- version: 1
+
+-- Truncate k_sol_core_logs explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_logs RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql
new file mode 100644
index 0000000..a04d9c6
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql
+-- version: 1
+
+-- Truncate k_sol_core_return_data explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_return_data RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql
new file mode 100644
index 0000000..1ed6d14
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql
+-- version: 1
+
+-- Truncate k_sol_core_transactions explicitly for maintenance.
+TRUNCATE TABLE k_sol_core_transactions RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql
new file mode 100644
index 0000000..67403be
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql
+-- version: 1
+
+-- Truncate k_sol_decode_coverage_declarations explicitly for maintenance.
+TRUNCATE TABLE k_sol_decode_coverage_declarations RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql
new file mode 100644
index 0000000..4b8bc94
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql
+-- version: 1
+
+-- Truncate k_sol_decode_coverage_observations explicitly for maintenance.
+TRUNCATE TABLE k_sol_decode_coverage_observations RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql
new file mode 100644
index 0000000..bdc9e51
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql
+-- version: 1
+
+-- Truncate k_sol_decode_events explicitly for maintenance.
+TRUNCATE TABLE k_sol_decode_events RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql
new file mode 100644
index 0000000..fc7df28
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql
+-- version: 1
+
+-- Truncate k_sol_mat_outputs explicitly for maintenance.
+TRUNCATE TABLE k_sol_mat_outputs RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql
new file mode 100644
index 0000000..cbb454b
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql
+-- version: 1
+
+-- Truncate k_sol_obs_account_observations explicitly for maintenance.
+TRUNCATE TABLE k_sol_obs_account_observations RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql
new file mode 100644
index 0000000..a0c88cf
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql
+-- version: 1
+
+-- Truncate k_sol_obs_transaction_observations explicitly for maintenance.
+TRUNCATE TABLE k_sol_obs_transaction_observations RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql
new file mode 100644
index 0000000..8793ab2
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql
+-- version: 1
+
+-- Truncate k_sol_ops_processing_ledger explicitly for maintenance.
+TRUNCATE TABLE k_sol_ops_processing_ledger RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql
new file mode 100644
index 0000000..5d5b8c4
--- /dev/null
+++ b/ks-store/migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql
+-- version: 1
+
+-- Truncate k_sol_raw_transactions explicitly for maintenance.
+TRUNCATE TABLE k_sol_raw_transactions RESTART IDENTITY CASCADE;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql
new file mode 100644
index 0000000..49ec601
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_keys_index_non_negative to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_keys_index_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_index_non_negative CHECK (account_index >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql
new file mode 100644
index 0000000..de1a72b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_keys_key_not_empty to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_keys_key_not_empty'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql
new file mode 100644
index 0000000..8d2f3ad
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_keys_signature_not_empty to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_keys_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql
new file mode 100644
index 0000000..90b5215
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_keys_slot_non_negative to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_keys_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql
new file mode 100644
index 0000000..bc583d2
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_keys_source to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_keys_source'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql
new file mode 100644
index 0000000..55e0cbf
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_account_not_empty to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_account_not_empty'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_account_not_empty CHECK (length(btrim(account_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql
new file mode 100644
index 0000000..bb44a07
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_hash_not_empty to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_hash_not_empty'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_hash_not_empty CHECK (length(btrim(data_hash)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql
new file mode 100644
index 0000000..91351c2
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_lamports_non_negative to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_lamports_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_lamports_non_negative CHECK (lamports >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql
new file mode 100644
index 0000000..3de5993
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_owner_not_empty to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_owner_not_empty'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_owner_not_empty CHECK (length(btrim(owner)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql
new file mode 100644
index 0000000..f34dec3
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_rent_epoch_non_negative to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_rent_epoch_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_rent_epoch_non_negative CHECK (rent_epoch >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql
new file mode 100644
index 0000000..1d326fd
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_slot_non_negative to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql
new file mode 100644
index 0000000..afa7c93
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_account_states_space_non_negative to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_account_states_space_non_negative'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_space_non_negative CHECK (space >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql
new file mode 100644
index 0000000..0db64d6
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_account_index_non_negative to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_account_index_non_negative'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql
new file mode 100644
index 0000000..006072b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_account_not_empty to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_account_not_empty'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql
new file mode 100644
index 0000000..e34ca4a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_index_non_negative to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_index_non_negative'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql
new file mode 100644
index 0000000..ae26591
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_kind to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_kind'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql
new file mode 100644
index 0000000..193ac7c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_mint_not_empty to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_mint_not_empty'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql
new file mode 100644
index 0000000..743a700
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_owner_not_empty to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_owner_not_empty'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql
new file mode 100644
index 0000000..c37bf9f
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_signature_not_empty to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql
new file mode 100644
index 0000000..74b1eba
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_balance_changes_slot_non_negative to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_balance_changes_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql
new file mode 100644
index 0000000..e848c04
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_parent_not_empty to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_parent_not_empty'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql
new file mode 100644
index 0000000..87ae30c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_path_not_empty to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_path_not_empty'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql
new file mode 100644
index 0000000..9ac794e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_processing_state to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_processing_state'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair.sql
new file mode 100644
index 0000000..de26a02
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_processor_pair to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_processor_pair'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_processor_pair CHECK ((processor_name IS NULL AND processor_version IS NULL) OR (length(btrim(processor_name)) > 0 AND length(btrim(processor_version)) > 0));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty.sql
new file mode 100644
index 0000000..f2dfeda
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_program_not_empty to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_program_not_empty'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty.sql
new file mode 100644
index 0000000..ec6f971
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_signature_not_empty to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative.sql
new file mode 100644
index 0000000..d6452a8
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_slot_non_negative to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive.sql
new file mode 100644
index 0000000..72045a7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive.sql
+-- version: 1
+
+-- Add ck_k_sol_core_inner_instructions_stack_height_positive to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_inner_instructions_stack_height_positive'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_stack_height_positive CHECK (stack_height IS NULL OR stack_height > 1);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty.sql
new file mode 100644
index 0000000..07d26ea
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_path_not_empty to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_path_not_empty'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state.sql
new file mode 100644
index 0000000..6c4904d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_processing_state to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_processing_state'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair.sql
new file mode 100644
index 0000000..a4254f8
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_processor_pair to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_processor_pair'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_processor_pair CHECK ((processor_name IS NULL AND processor_version IS NULL) OR (length(btrim(processor_name)) > 0 AND length(btrim(processor_version)) > 0));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty.sql
new file mode 100644
index 0000000..42eefc3
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_program_not_empty to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_program_not_empty'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty.sql
new file mode 100644
index 0000000..119da91
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_signature_not_empty to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative.sql
new file mode 100644
index 0000000..02f94ad
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_slot_non_negative to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive.sql
new file mode 100644
index 0000000..7a6d2a5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive.sql
+-- version: 1
+
+-- Add ck_k_sol_core_instructions_stack_height_positive to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_instructions_stack_height_positive'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT ck_k_sol_core_instructions_stack_height_positive CHECK (stack_height IS NULL OR stack_height > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative.sql
new file mode 100644
index 0000000..8b19880
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_logs_index_non_negative to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_logs_index_non_negative'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT ck_k_sol_core_logs_index_non_negative CHECK (log_index >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty.sql
new file mode 100644
index 0000000..4b9edc5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_logs_path_not_empty to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_logs_path_not_empty'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT ck_k_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty.sql
new file mode 100644
index 0000000..56fc3de
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_logs_program_not_empty to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_logs_program_not_empty'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT ck_k_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty.sql
new file mode 100644
index 0000000..dbead85
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_logs_signature_not_empty to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_logs_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT ck_k_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative.sql
new file mode 100644
index 0000000..10b6d14
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_logs_slot_non_negative to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_logs_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT ck_k_sol_core_logs_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty.sql
new file mode 100644
index 0000000..8bed928
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_return_data_program_not_empty to k_sol_core_return_data when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_return_data_program_not_empty'
+ AND conrelid = to_regclass('k_sol_core_return_data')
+ ) THEN
+ ALTER TABLE k_sol_core_return_data ADD CONSTRAINT ck_k_sol_core_return_data_program_not_empty CHECK (length(btrim(program_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty.sql
new file mode 100644
index 0000000..543f5a4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_return_data_signature_not_empty to k_sol_core_return_data when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_return_data_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_return_data')
+ ) THEN
+ ALTER TABLE k_sol_core_return_data ADD CONSTRAINT ck_k_sol_core_return_data_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative.sql
new file mode 100644
index 0000000..8bb1638
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_return_data_slot_non_negative to k_sol_core_return_data when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_return_data_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_return_data')
+ ) THEN
+ ALTER TABLE k_sol_core_return_data ADD CONSTRAINT ck_k_sol_core_return_data_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty.sql
new file mode 100644
index 0000000..c796c27
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_core_transactions_signature_not_empty to k_sol_core_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_transactions_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_core_transactions')
+ ) THEN
+ ALTER TABLE k_sol_core_transactions ADD CONSTRAINT ck_k_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative.sql
new file mode 100644
index 0000000..cf60632
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_core_transactions_slot_non_negative to k_sol_core_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_core_transactions_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_core_transactions')
+ ) THEN
+ ALTER TABLE k_sol_core_transactions ADD CONSTRAINT ck_k_sol_core_transactions_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative.sql
new file mode 100644
index 0000000..e7b1b05
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_decode_coverage_observations_counts_non_negative to k_sol_decode_coverage_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_decode_coverage_observations_counts_non_negative'
+ AND conrelid = to_regclass('k_sol_decode_coverage_observations')
+ ) THEN
+ ALTER TABLE k_sol_decode_coverage_observations ADD CONSTRAINT ck_k_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative.sql
new file mode 100644
index 0000000..acd1e4d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_decode_coverage_observations_slot_non_negative to k_sol_decode_coverage_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_decode_coverage_observations_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_decode_coverage_observations')
+ ) THEN
+ ALTER TABLE k_sol_decode_coverage_observations ADD CONSTRAINT ck_k_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed.sql
new file mode 100644
index 0000000..f48b8ad
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed.sql
+-- version: 1
+
+-- Add ck_k_sol_decode_events_failed_not_committed to k_sol_decode_events when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_decode_events_failed_not_committed'
+ AND conrelid = to_regclass('k_sol_decode_events')
+ ) THEN
+ ALTER TABLE k_sol_decode_events ADD CONSTRAINT ck_k_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance.sql
new file mode 100644
index 0000000..7300a29
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance.sql
+-- version: 1
+
+-- Add ck_k_sol_decode_events_schema_provenance to k_sol_decode_events when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_decode_events_schema_provenance'
+ AND conrelid = to_regclass('k_sol_decode_events')
+ ) THEN
+ ALTER TABLE k_sol_decode_events ADD CONSTRAINT ck_k_sol_decode_events_schema_provenance CHECK ((schema_kind IS NULL AND schema_id IS NULL AND schema_version IS NULL AND schema_hash IS NULL) OR (length(btrim(schema_kind)) > 0 AND length(btrim(schema_id)) > 0 AND length(btrim(schema_hash)) > 0 AND (schema_version IS NULL OR length(btrim(schema_version)) > 0)));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative.sql
new file mode 100644
index 0000000..bb2b850
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_decode_events_slot_non_negative to k_sol_decode_events when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_decode_events_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_decode_events')
+ ) THEN
+ ALTER TABLE k_sol_decode_events ADD CONSTRAINT ck_k_sol_decode_events_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative.sql
new file mode 100644
index 0000000..04f6fe3
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_mat_outputs_slot_non_negative to k_sol_mat_outputs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_mat_outputs_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_mat_outputs')
+ ) THEN
+ ALTER TABLE k_sol_mat_outputs ADD CONSTRAINT ck_k_sol_mat_outputs_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty.sql
new file mode 100644
index 0000000..625a2f0
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_account_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_account_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_account_not_empty CHECK (length(btrim(account_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty.sql
new file mode 100644
index 0000000..d1d914c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_commitment_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_commitment_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty.sql
new file mode 100644
index 0000000..754d944
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_endpoint_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_endpoint_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty.sql
new file mode 100644
index 0000000..44e2963
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_error_code_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_error_code_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty.sql
new file mode 100644
index 0000000..148ad56
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_error_message_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_error_message_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty.sql
new file mode 100644
index 0000000..37f9808
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_filter_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_filter_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty.sql
new file mode 100644
index 0000000..a73f942
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_hash_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_hash_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_hash_not_empty CHECK (data_hash IS NULL OR length(btrim(data_hash)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty.sql
new file mode 100644
index 0000000..1e6d0c2
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_key_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_key_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative.sql
new file mode 100644
index 0000000..02ee908
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_lamports_non_negative to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_lamports_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_lamports_non_negative CHECK (lamports IS NULL OR lamports >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty.sql
new file mode 100644
index 0000000..04904d9
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_method_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_method_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin.sql
new file mode 100644
index 0000000..59a4bc7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_origin to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_origin'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty.sql
new file mode 100644
index 0000000..292e265
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_owner_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_owner_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative.sql
new file mode 100644
index 0000000..5c8e7c0
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_payload_size_non_negative to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_payload_size_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty.sql
new file mode 100644
index 0000000..3dc69fe
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_protocol_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_protocol_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty.sql
new file mode 100644
index 0000000..8cbc73b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_provider_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_provider_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_provider_not_empty CHECK (length(btrim(provider)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative.sql
new file mode 100644
index 0000000..60252b4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_rent_epoch_non_negative to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_rent_epoch_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_rent_epoch_non_negative CHECK (rent_epoch IS NULL OR rent_epoch >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty.sql
new file mode 100644
index 0000000..2f93611
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_session_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_session_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative.sql
new file mode 100644
index 0000000..266df88
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_slot_non_negative to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_slot_non_negative CHECK (context_slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty.sql
new file mode 100644
index 0000000..694c253
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_source_hash_not_empty to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_source_hash_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_source_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative.sql
new file mode 100644
index 0000000..12c3555
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_space_non_negative to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_space_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_space_non_negative CHECK (space IS NULL OR space >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape.sql
new file mode 100644
index 0000000..bae5b2a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape.sql
+-- version: 1
+
+-- Require a captured account state to be either complete or absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_state_shape'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_state_shape CHECK ((owner IS NULL AND lamports IS NULL AND executable IS NULL AND rent_epoch IS NULL AND space IS NULL AND data_base64 IS NULL AND data_hash IS NULL) OR (owner IS NOT NULL AND lamports IS NOT NULL AND executable IS NOT NULL AND rent_epoch IS NOT NULL AND space IS NOT NULL AND data_base64 IS NOT NULL AND data_hash IS NOT NULL));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_status.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_status.sql
new file mode 100644
index 0000000..c63b36d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_status.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_status.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_account_observations_status to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_account_observations_status'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT ck_k_sol_obs_account_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty.sql
new file mode 100644
index 0000000..d960427
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_commitment_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_commitment_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty.sql
new file mode 100644
index 0000000..9a6abef
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_endpoint_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_endpoint_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty.sql
new file mode 100644
index 0000000..a060cbb
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_error_code_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_error_code_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty.sql
new file mode 100644
index 0000000..0e810c9
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_error_message_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_error_message_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty.sql
new file mode 100644
index 0000000..8f1c274
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_filter_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_filter_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty.sql
new file mode 100644
index 0000000..f6bc074
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_hash_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_hash_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty.sql
new file mode 100644
index 0000000..35c0912
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_key_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_key_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty.sql
new file mode 100644
index 0000000..94fbd12
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_method_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_method_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin.sql
new file mode 100644
index 0000000..01a7493
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_origin to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_origin'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative.sql
new file mode 100644
index 0000000..532fe00
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_payload_size_non_negative to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_payload_size_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty.sql
new file mode 100644
index 0000000..bacbcb6
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_protocol_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_protocol_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty.sql
new file mode 100644
index 0000000..c2ca939
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_provider_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_provider_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty.sql
new file mode 100644
index 0000000..9b5b377
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_session_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_session_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty.sql
new file mode 100644
index 0000000..a96d713
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_signature_not_empty to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative.sql
new file mode 100644
index 0000000..8ed4250
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_slot_non_negative to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status.sql
new file mode 100644
index 0000000..3bf27ac
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status.sql
+-- version: 1
+
+-- Add ck_k_sol_obs_transaction_observations_status to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_obs_transaction_observations_status'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT ck_k_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative.sql
new file mode 100644
index 0000000..d716b41
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_attempt_count_non_negative to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_attempt_count_non_negative'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty.sql
new file mode 100644
index 0000000..2af653d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_error_code_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_error_code_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty.sql
new file mode 100644
index 0000000..e131edc
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_error_message_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_error_message_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty.sql
new file mode 100644
index 0000000..119d994
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_input_hash_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_input_hash_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty.sql
new file mode 100644
index 0000000..86cc15f
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_input_key_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_input_key_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty.sql
new file mode 100644
index 0000000..644858e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_processor_name_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_processor_name_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty.sql
new file mode 100644
index 0000000..81ba4d7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_processor_version_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_processor_version_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty.sql
new file mode 100644
index 0000000..d7a1ead
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_stage_not_empty to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_stage_not_empty'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status.sql
new file mode 100644
index 0000000..c155bb8
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status.sql
+-- version: 1
+
+-- Add ck_k_sol_ops_processing_ledger_status to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_ops_processing_ledger_status'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT ck_k_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive.sql
new file mode 100644
index 0000000..c75a3a7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_format_version_positive to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_format_version_positive'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json.sql
new file mode 100644
index 0000000..abc62c5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_full_has_json to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_full_has_json'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state.sql
new file mode 100644
index 0000000..d423c7f
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_processing_state to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_processing_state'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state.sql
new file mode 100644
index 0000000..3af8fbc
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_retention_state to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_retention_state'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged'));
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty.sql
new file mode 100644
index 0000000..6a6a207
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_signature_not_empty to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_signature_not_empty'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative.sql
new file mode 100644
index 0000000..707d26f
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative.sql
+-- version: 1
+
+-- Add ck_k_sol_raw_transactions_slot_non_negative to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'ck_k_sol_raw_transactions_slot_non_negative'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT ck_k_sol_raw_transactions_slot_non_negative CHECK (slot >= 0);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction.sql
new file mode 100644
index 0000000..74dea08
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_account_keys_transaction to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_account_keys_transaction'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT fk_k_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_states_observation.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_states_observation.sql
new file mode 100644
index 0000000..c7e5f79
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_states_observation.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_states_observation.sql
+-- version: 1
+
+-- Add fk_k_sol_core_account_states_observation to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_account_states_observation'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT fk_k_sol_core_account_states_observation FOREIGN KEY (source_observation_id) REFERENCES k_sol_obs_account_observations(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction.sql
new file mode 100644
index 0000000..b68ef0d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_balance_changes_transaction to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_balance_changes_transaction'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT fk_k_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction.sql
new file mode 100644
index 0000000..09254cb
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_inner_instructions_transaction to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_inner_instructions_transaction'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT fk_k_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_instructions_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_instructions_transaction.sql
new file mode 100644
index 0000000..b017433
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_instructions_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_instructions_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_instructions_transaction to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_instructions_transaction'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT fk_k_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_logs_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_logs_transaction.sql
new file mode 100644
index 0000000..223af18
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_logs_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_logs_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_logs_transaction to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_logs_transaction'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT fk_k_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_return_data_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_return_data_transaction.sql
new file mode 100644
index 0000000..9ff4d7e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_return_data_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_return_data_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_return_data_transaction to k_sol_core_return_data when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_return_data_transaction'
+ AND conrelid = to_regclass('k_sol_core_return_data')
+ ) THEN
+ ALTER TABLE k_sol_core_return_data ADD CONSTRAINT fk_k_sol_core_return_data_transaction FOREIGN KEY (transaction_id) REFERENCES k_sol_core_transactions(id) ON DELETE CASCADE;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction.sql
new file mode 100644
index 0000000..d6ce043
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_core_transactions_raw_transaction to k_sol_core_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_core_transactions_raw_transaction'
+ AND conrelid = to_regclass('k_sol_core_transactions')
+ ) THEN
+ ALTER TABLE k_sol_core_transactions ADD CONSTRAINT fk_k_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES k_sol_raw_transactions(id) ON DELETE SET NULL;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction.sql
new file mode 100644
index 0000000..fe046ab
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction.sql
+-- version: 1
+
+-- Add fk_k_sol_obs_transaction_observations_raw_transaction to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'fk_k_sol_obs_transaction_observations_raw_transaction'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT fk_k_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES k_sol_raw_transactions(id) ON DELETE SET NULL;
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_keys.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_keys.sql
new file mode 100644
index 0000000..2f7376e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_keys.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_keys.sql
+-- version: 1
+
+-- Add pk_k_sol_core_account_keys to k_sol_core_account_keys when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_account_keys'
+ AND conrelid = to_regclass('k_sol_core_account_keys')
+ ) THEN
+ ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT pk_k_sol_core_account_keys PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_states.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_states.sql
new file mode 100644
index 0000000..037231a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_states.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_states.sql
+-- version: 1
+
+-- Add pk_k_sol_core_account_states to k_sol_core_account_states when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_account_states'
+ AND conrelid = to_regclass('k_sol_core_account_states')
+ ) THEN
+ ALTER TABLE k_sol_core_account_states ADD CONSTRAINT pk_k_sol_core_account_states PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_balance_changes.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_balance_changes.sql
new file mode 100644
index 0000000..c9c7303
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_balance_changes.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_balance_changes.sql
+-- version: 1
+
+-- Add pk_k_sol_core_balance_changes to k_sol_core_balance_changes when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_balance_changes'
+ AND conrelid = to_regclass('k_sol_core_balance_changes')
+ ) THEN
+ ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT pk_k_sol_core_balance_changes PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_inner_instructions.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_inner_instructions.sql
new file mode 100644
index 0000000..569a5cc
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_inner_instructions.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_inner_instructions.sql
+-- version: 1
+
+-- Add pk_k_sol_core_inner_instructions to k_sol_core_inner_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_inner_instructions'
+ AND conrelid = to_regclass('k_sol_core_inner_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT pk_k_sol_core_inner_instructions PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_instructions.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_instructions.sql
new file mode 100644
index 0000000..1a59c05
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_instructions.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_instructions.sql
+-- version: 1
+
+-- Add pk_k_sol_core_instructions to k_sol_core_instructions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_instructions'
+ AND conrelid = to_regclass('k_sol_core_instructions')
+ ) THEN
+ ALTER TABLE k_sol_core_instructions ADD CONSTRAINT pk_k_sol_core_instructions PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_logs.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_logs.sql
new file mode 100644
index 0000000..a37bd7e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_logs.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_logs.sql
+-- version: 1
+
+-- Add pk_k_sol_core_logs to k_sol_core_logs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_logs'
+ AND conrelid = to_regclass('k_sol_core_logs')
+ ) THEN
+ ALTER TABLE k_sol_core_logs ADD CONSTRAINT pk_k_sol_core_logs PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_return_data.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_return_data.sql
new file mode 100644
index 0000000..fea5379
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_return_data.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_return_data.sql
+-- version: 1
+
+-- Add pk_k_sol_core_return_data to k_sol_core_return_data when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_return_data'
+ AND conrelid = to_regclass('k_sol_core_return_data')
+ ) THEN
+ ALTER TABLE k_sol_core_return_data ADD CONSTRAINT pk_k_sol_core_return_data PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_transactions.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_transactions.sql
new file mode 100644
index 0000000..5b0bca2
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_transactions.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_transactions.sql
+-- version: 1
+
+-- Add pk_k_sol_core_transactions to k_sol_core_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_core_transactions'
+ AND conrelid = to_regclass('k_sol_core_transactions')
+ ) THEN
+ ALTER TABLE k_sol_core_transactions ADD CONSTRAINT pk_k_sol_core_transactions PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations.sql
new file mode 100644
index 0000000..5fd2a3a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations.sql
+-- version: 1
+
+-- Add pk_k_sol_decode_coverage_declarations to k_sol_decode_coverage_declarations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_decode_coverage_declarations'
+ AND conrelid = to_regclass('k_sol_decode_coverage_declarations')
+ ) THEN
+ ALTER TABLE k_sol_decode_coverage_declarations ADD CONSTRAINT pk_k_sol_decode_coverage_declarations PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_observations.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_observations.sql
new file mode 100644
index 0000000..2535695
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_observations.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_observations.sql
+-- version: 1
+
+-- Add pk_k_sol_decode_coverage_observations to k_sol_decode_coverage_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_decode_coverage_observations'
+ AND conrelid = to_regclass('k_sol_decode_coverage_observations')
+ ) THEN
+ ALTER TABLE k_sol_decode_coverage_observations ADD CONSTRAINT pk_k_sol_decode_coverage_observations PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_events.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_events.sql
new file mode 100644
index 0000000..5c40758
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_events.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_events.sql
+-- version: 1
+
+-- Add pk_k_sol_decode_events to k_sol_decode_events when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_decode_events'
+ AND conrelid = to_regclass('k_sol_decode_events')
+ ) THEN
+ ALTER TABLE k_sol_decode_events ADD CONSTRAINT pk_k_sol_decode_events PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_mat_outputs.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_mat_outputs.sql
new file mode 100644
index 0000000..5fb878a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_mat_outputs.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_mat_outputs.sql
+-- version: 1
+
+-- Add pk_k_sol_mat_outputs to k_sol_mat_outputs when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_mat_outputs'
+ AND conrelid = to_regclass('k_sol_mat_outputs')
+ ) THEN
+ ALTER TABLE k_sol_mat_outputs ADD CONSTRAINT pk_k_sol_mat_outputs PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_account_observations.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_account_observations.sql
new file mode 100644
index 0000000..e0abcc5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_account_observations.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_account_observations.sql
+-- version: 1
+
+-- Add pk_k_sol_obs_account_observations to k_sol_obs_account_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_obs_account_observations'
+ AND conrelid = to_regclass('k_sol_obs_account_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_account_observations ADD CONSTRAINT pk_k_sol_obs_account_observations PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_transaction_observations.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_transaction_observations.sql
new file mode 100644
index 0000000..a68046e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_transaction_observations.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_transaction_observations.sql
+-- version: 1
+
+-- Add pk_k_sol_obs_transaction_observations to k_sol_obs_transaction_observations when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_obs_transaction_observations'
+ AND conrelid = to_regclass('k_sol_obs_transaction_observations')
+ ) THEN
+ ALTER TABLE k_sol_obs_transaction_observations ADD CONSTRAINT pk_k_sol_obs_transaction_observations PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_ops_processing_ledger.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_ops_processing_ledger.sql
new file mode 100644
index 0000000..40e03b0
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_ops_processing_ledger.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_ops_processing_ledger.sql
+-- version: 1
+
+-- Add pk_k_sol_ops_processing_ledger to k_sol_ops_processing_ledger when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_ops_processing_ledger'
+ AND conrelid = to_regclass('k_sol_ops_processing_ledger')
+ ) THEN
+ ALTER TABLE k_sol_ops_processing_ledger ADD CONSTRAINT pk_k_sol_ops_processing_ledger PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_raw_transactions.sql b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_raw_transactions.sql
new file mode 100644
index 0000000..ed81fa4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_raw_transactions.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_raw_transactions.sql
+-- version: 1
+
+-- Add pk_k_sol_raw_transactions to k_sol_raw_transactions when absent.
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'pk_k_sol_raw_transactions'
+ AND conrelid = to_regclass('k_sol_raw_transactions')
+ ) THEN
+ ALTER TABLE k_sol_raw_transactions ADD CONSTRAINT pk_k_sol_raw_transactions PRIMARY KEY (id);
+ END IF;
+END
+$$;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_keys_key.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_keys_key.sql
new file mode 100644
index 0000000..db28c30
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_keys_key.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_keys_key.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_account_keys_key when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_account_keys_key ON k_sol_core_account_keys (account_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_account_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_account_slot.sql
new file mode 100644
index 0000000..b561f17
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_account_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_account_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_account_states_account_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_account_states_account_slot ON k_sol_core_account_states (account_key, slot DESC, id DESC);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_owner.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_owner.sql
new file mode 100644
index 0000000..2a72b32
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_owner.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_owner.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_account_states_owner when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_account_states_owner ON k_sol_core_account_states (owner, slot DESC, id DESC);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_account.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_account.sql
new file mode 100644
index 0000000..4e263f0
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_account.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_account.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_balance_changes_account when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_balance_changes_account ON k_sol_core_balance_changes (account_key) WHERE account_key IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_mint.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_mint.sql
new file mode 100644
index 0000000..21ef8d2
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_mint.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_mint.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_balance_changes_mint when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_balance_changes_mint ON k_sol_core_balance_changes (mint) WHERE mint IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_owner.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_owner.sql
new file mode 100644
index 0000000..eb3d4ec
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_owner.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_owner.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_balance_changes_owner when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_balance_changes_owner ON k_sol_core_balance_changes (owner) WHERE owner IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_parent.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_parent.sql
new file mode 100644
index 0000000..bceb0d8
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_parent.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_parent.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_inner_instructions_parent when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_inner_instructions_parent ON k_sol_core_inner_instructions (signature, parent_instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_processing.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_processing.sql
new file mode 100644
index 0000000..ecca886
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_processing.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_processing.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_inner_instructions_processing when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_inner_instructions_processing ON k_sol_core_inner_instructions (processing_state, slot, signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_program.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_program.sql
new file mode 100644
index 0000000..5cd9c77
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_program.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_program.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_inner_instructions_program when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_inner_instructions_program ON k_sol_core_inner_instructions (program_id, slot, signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_processing.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_processing.sql
new file mode 100644
index 0000000..bfd161e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_processing.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_processing.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_instructions_processing when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_instructions_processing ON k_sol_core_instructions (processing_state, slot, signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_program.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_program.sql
new file mode 100644
index 0000000..9bdae2e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_program.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_program.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_instructions_program when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_instructions_program ON k_sol_core_instructions (program_id, slot, signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_slot.sql
new file mode 100644
index 0000000..bc57b2e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_instructions_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_instructions_slot ON k_sol_core_instructions (slot, signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_path.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_path.sql
new file mode 100644
index 0000000..5fd6203
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_path.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_path.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_logs_path when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_logs_path ON k_sol_core_logs (signature, instruction_path) WHERE instruction_path IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_program.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_program.sql
new file mode 100644
index 0000000..228f29d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_program.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_program.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_logs_program when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_logs_program ON k_sol_core_logs (program_id) WHERE program_id IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_return_data_program.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_return_data_program.sql
new file mode 100644
index 0000000..1380e5b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_return_data_program.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_return_data_program.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_return_data_program when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_return_data_program ON k_sol_core_return_data (program_id, slot, signature);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_created_at.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_created_at.sql
new file mode 100644
index 0000000..bd1bd2c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_created_at.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_created_at.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_transactions_created_at when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_transactions_created_at ON k_sol_core_transactions (created_at);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_slot.sql
new file mode 100644
index 0000000..8d09b64
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_core_transactions_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_core_transactions_slot ON k_sol_core_transactions (slot);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program.sql
new file mode 100644
index 0000000..bb5f76d
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_coverage_declarations_program when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_coverage_declarations_program ON k_sol_decode_coverage_declarations (program_id, processor_name, processor_version);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry.sql
new file mode 100644
index 0000000..c4a74f7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_coverage_observations_entry when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_coverage_observations_entry ON k_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status.sql
new file mode 100644
index 0000000..62c455b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_coverage_observations_program_status when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_coverage_observations_program_status ON k_sol_decode_coverage_observations (program_id, status, transaction_failed);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_family_commit.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_family_commit.sql
new file mode 100644
index 0000000..0c6c8c6
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_family_commit.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_family_commit.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_events_family_commit when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_events_family_commit ON k_sol_decode_events (event_family, transaction_failed, observation_committed);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_program_surface.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_program_surface.sql
new file mode 100644
index 0000000..d976bbe
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_program_surface.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_program_surface.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_events_program_surface when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_events_program_surface ON k_sol_decode_events (program_id, surface_code, event_code);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_signature_path.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_signature_path.sql
new file mode 100644
index 0000000..c6ecc1e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_signature_path.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_signature_path.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_decode_events_signature_path when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_decode_events_signature_path ON k_sol_decode_events (signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot.sql
new file mode 100644
index 0000000..f94c406
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_mat_outputs_processor_family_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_mat_outputs_processor_family_slot ON k_sol_mat_outputs (processor_name, materialized_family, slot DESC, id DESC);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_signature_family.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_signature_family.sql
new file mode 100644
index 0000000..0d06a41
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_signature_family.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_signature_family.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_mat_outputs_signature_family when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_mat_outputs_signature_family ON k_sol_mat_outputs (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot.sql
new file mode 100644
index 0000000..5531b76
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_account_observations_account_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_account_observations_account_slot ON k_sol_obs_account_observations (account_key, context_slot DESC, id DESC);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method.sql
new file mode 100644
index 0000000..658d337
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_account_observations_provider_method when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_account_observations_provider_method ON k_sol_obs_account_observations (provider, acquisition_method);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_received_at.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_received_at.sql
new file mode 100644
index 0000000..f8add2a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_received_at.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_received_at.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_account_observations_received_at when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_account_observations_received_at ON k_sol_obs_account_observations (received_at);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_status.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_status.sql
new file mode 100644
index 0000000..8091ed9
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_status.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_status.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_account_observations_status when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_account_observations_status ON k_sol_obs_account_observations (status);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method.sql
new file mode 100644
index 0000000..5ffb79a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_provider_method when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_provider_method ON k_sol_obs_transaction_observations (provider, acquisition_method);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction.sql
new file mode 100644
index 0000000..447d1d7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_raw_transaction when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_raw_transaction ON k_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at.sql
new file mode 100644
index 0000000..87482b9
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_received_at when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_received_at ON k_sol_obs_transaction_observations (received_at);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature.sql
new file mode 100644
index 0000000..a547872
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_signature when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_signature ON k_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot.sql
new file mode 100644
index 0000000..0cf867a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_slot ON k_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_status.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_status.sql
new file mode 100644
index 0000000..60c9e5a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_status.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_status.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_obs_transaction_observations_status when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_obs_transaction_observations_status ON k_sol_obs_transaction_observations (status);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash.sql
new file mode 100644
index 0000000..a37a9da
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_ops_processing_ledger_input_hash when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_ops_processing_ledger_input_hash ON k_sol_ops_processing_ledger (input_hash);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input.sql
new file mode 100644
index 0000000..57de282
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_ops_processing_ledger_stage_input when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_ops_processing_ledger_stage_input ON k_sol_ops_processing_ledger (stage, input_key, updated_at DESC, id DESC);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_status.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_status.sql
new file mode 100644
index 0000000..dc95746
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_status.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_status.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_ops_processing_ledger_status when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_ops_processing_ledger_status ON k_sol_ops_processing_ledger (stage, processor_name, status, updated_at);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash.sql
new file mode 100644
index 0000000..54d13a5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_raw_transactions_canonical_hash when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_raw_transactions_canonical_hash ON k_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL;
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_created_at.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_created_at.sql
new file mode 100644
index 0000000..40d528c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_created_at.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_created_at.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_raw_transactions_created_at when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_raw_transactions_created_at ON k_sol_raw_transactions (created_at);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing.sql
new file mode 100644
index 0000000..851d4b3
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_raw_transactions_processing when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_raw_transactions_processing ON k_sol_raw_transactions (processing_state);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot.sql
new file mode 100644
index 0000000..70c6caf
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_raw_transactions_processing_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_raw_transactions_processing_slot ON k_sol_raw_transactions (processing_state, slot, signature);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql
new file mode 100644
index 0000000..ee536fb
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql
+-- version: 1
+
+-- Create PostgreSQL index ix_k_sol_raw_transactions_slot when absent.
+CREATE INDEX IF NOT EXISTS ix_k_sol_raw_transactions_slot ON k_sol_raw_transactions (slot);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_keys_sig_index.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_keys_sig_index.sql
new file mode 100644
index 0000000..110cc7c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_keys_sig_index.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_keys_sig_index.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_account_keys_sig_index when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_account_keys_sig_index ON k_sol_core_account_keys (signature, account_index);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_states_observation.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_states_observation.sql
new file mode 100644
index 0000000..f8ec604
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_states_observation.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_states_observation.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_account_states_observation when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_account_states_observation ON k_sol_core_account_states (source_observation_id);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index.sql
new file mode 100644
index 0000000..aa51740
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_balance_changes_sig_index when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_balance_changes_sig_index ON k_sol_core_balance_changes (signature, balance_change_index);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path.sql
new file mode 100644
index 0000000..703f2ff
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_inner_instructions_sig_path when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_inner_instructions_sig_path ON k_sol_core_inner_instructions (signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_instructions_sig_path.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_instructions_sig_path.sql
new file mode 100644
index 0000000..d95694a
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_instructions_sig_path.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_instructions_sig_path.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_instructions_sig_path when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_instructions_sig_path ON k_sol_core_instructions (signature, instruction_path);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_logs_sig_index.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_logs_sig_index.sql
new file mode 100644
index 0000000..81384c4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_logs_sig_index.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_logs_sig_index.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_logs_sig_index when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_logs_sig_index ON k_sol_core_logs (signature, log_index);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_return_data_signature.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_return_data_signature.sql
new file mode 100644
index 0000000..47686b0
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_return_data_signature.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_return_data_signature.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_return_data_signature when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_return_data_signature ON k_sol_core_return_data (signature);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_transactions_signature.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_transactions_signature.sql
new file mode 100644
index 0000000..2bc66e5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_transactions_signature.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_transactions_signature.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_core_transactions_signature when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_core_transactions_signature ON k_sol_core_transactions (signature);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity.sql
new file mode 100644
index 0000000..8f47e71
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_decode_coverage_declarations_identity when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_decode_coverage_declarations_identity ON k_sol_decode_coverage_declarations (processor_name, processor_version, program_id, COALESCE(surface_code, ''), entry_kind, entry_code, COALESCE(discriminator_hex, ''));
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity.sql
new file mode 100644
index 0000000..77b3509
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_decode_coverage_observations_identity when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_decode_coverage_observations_identity ON k_sol_decode_coverage_observations (processor_name, processor_version, input_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_events_processor_input_event.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_events_processor_input_event.sql
new file mode 100644
index 0000000..c2c9758
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_events_processor_input_event.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_events_processor_input_event.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_decode_events_processor_input_event when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_decode_events_processor_input_event ON k_sol_decode_events (processor_name, processor_version, input_key, event_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output.sql
new file mode 100644
index 0000000..765a6a4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_mat_outputs_processor_input_output when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_mat_outputs_processor_input_output ON k_sol_mat_outputs (processor_name, processor_version, input_key, output_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_account_observations_key.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_account_observations_key.sql
new file mode 100644
index 0000000..9fd845b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_account_observations_key.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_account_observations_key.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_obs_account_observations_key when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_obs_account_observations_key ON k_sol_obs_account_observations (observation_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_transaction_observations_key.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_transaction_observations_key.sql
new file mode 100644
index 0000000..cbc8208
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_transaction_observations_key.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_transaction_observations_key.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_obs_transaction_observations_key when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_obs_transaction_observations_key ON k_sol_obs_transaction_observations (observation_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity.sql
new file mode 100644
index 0000000..4fcfe7b
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_ops_processing_ledger_identity when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_ops_processing_ledger_identity ON k_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key);
diff --git a/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_raw_transactions_signature.sql b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_raw_transactions_signature.sql
new file mode 100644
index 0000000..6e471eb
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_raw_transactions_signature.sql
@@ -0,0 +1,5 @@
+-- file: migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_raw_transactions_signature.sql
+-- version: 1
+
+-- Create PostgreSQL index ux_k_sol_raw_transactions_signature when absent.
+CREATE UNIQUE INDEX IF NOT EXISTS ux_k_sol_raw_transactions_signature ON k_sol_raw_transactions (signature);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_keys.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_keys.sql
new file mode 100644
index 0000000..f539674
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_keys.sql
@@ -0,0 +1,17 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_keys.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_account_keys table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_account_keys (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ account_index INTEGER NOT NULL,
+ account_key TEXT NOT NULL,
+ source TEXT NOT NULL,
+ writable BOOLEAN NOT NULL,
+ signer BOOLEAN NOT NULL,
+ executable BOOLEAN,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_states.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_states.sql
new file mode 100644
index 0000000..207a410
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_states.sql
@@ -0,0 +1,18 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_states.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_account_states table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_account_states (
+ id BIGSERIAL,
+ source_observation_id BIGINT NOT NULL,
+ account_key TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ owner TEXT NOT NULL,
+ lamports NUMERIC(20,0) NOT NULL,
+ executable BOOLEAN NOT NULL,
+ rent_epoch NUMERIC(20,0) NOT NULL,
+ space BIGINT NOT NULL,
+ data_base64 TEXT NOT NULL,
+ data_hash TEXT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_balance_changes.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_balance_changes.sql
new file mode 100644
index 0000000..025be45
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_balance_changes.sql
@@ -0,0 +1,20 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_balance_changes.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_balance_changes table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_balance_changes (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ balance_change_index INTEGER NOT NULL,
+ balance_kind TEXT NOT NULL,
+ account_index INTEGER,
+ account_key TEXT,
+ mint TEXT,
+ owner TEXT,
+ pre_balance_json JSONB,
+ post_balance_json JSONB,
+ delta_json JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_inner_instructions.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_inner_instructions.sql
new file mode 100644
index 0000000..f562be1
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_inner_instructions.sql
@@ -0,0 +1,23 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_inner_instructions.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_inner_instructions table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_inner_instructions (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ parent_instruction_path TEXT NOT NULL,
+ instruction_path TEXT NOT NULL,
+ program_id TEXT NOT NULL,
+ stack_height INTEGER,
+ accounts_json JSONB NOT NULL,
+ payload_json JSONB,
+ payload_json_hash TEXT,
+ processing_state TEXT NOT NULL DEFAULT 'pending',
+ processor_name TEXT,
+ processor_version TEXT,
+ lifecycle_reason TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_instructions.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_instructions.sql
new file mode 100644
index 0000000..d4923b5
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_instructions.sql
@@ -0,0 +1,22 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_instructions.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_instructions table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_instructions (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ instruction_path TEXT NOT NULL,
+ program_id TEXT NOT NULL,
+ stack_height INTEGER,
+ accounts_json JSONB NOT NULL,
+ payload_json JSONB,
+ payload_json_hash TEXT,
+ processing_state TEXT NOT NULL DEFAULT 'pending',
+ processor_name TEXT,
+ processor_version TEXT,
+ lifecycle_reason TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_logs.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_logs.sql
new file mode 100644
index 0000000..31c46e4
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_logs.sql
@@ -0,0 +1,16 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_logs.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_logs table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_logs (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ log_index INTEGER NOT NULL,
+ instruction_path TEXT,
+ program_id TEXT,
+ log_text TEXT,
+ log_text_hash TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_return_data.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_return_data.sql
new file mode 100644
index 0000000..1bd1ca9
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_return_data.sql
@@ -0,0 +1,13 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_return_data.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_return_data table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_return_data (
+ id BIGSERIAL,
+ transaction_id BIGINT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ program_id TEXT NOT NULL,
+ data_base64 TEXT NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_transactions.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_transactions.sql
new file mode 100644
index 0000000..fdef369
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_transactions.sql
@@ -0,0 +1,15 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_transactions.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_core_transactions table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_core_transactions (
+ id BIGSERIAL,
+ raw_transaction_id BIGINT,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ block_time BIGINT,
+ failed BOOLEAN NOT NULL DEFAULT FALSE,
+ err_json JSONB,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_declarations.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_declarations.sql
new file mode 100644
index 0000000..69e531c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_declarations.sql
@@ -0,0 +1,17 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_declarations.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_decode_coverage_declarations table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_decode_coverage_declarations (
+ id BIGSERIAL,
+ processor_name TEXT NOT NULL,
+ processor_version TEXT NOT NULL,
+ program_id TEXT NOT NULL,
+ surface_code TEXT,
+ entry_kind TEXT NOT NULL,
+ entry_code TEXT NOT NULL,
+ discriminator_hex TEXT,
+ historical BOOLEAN NOT NULL DEFAULT FALSE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_observations.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_observations.sql
new file mode 100644
index 0000000..5d97981
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_observations.sql
@@ -0,0 +1,26 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_observations.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_decode_coverage_observations table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_decode_coverage_observations (
+ id BIGSERIAL,
+ processor_name TEXT NOT NULL,
+ processor_version TEXT NOT NULL,
+ input_key TEXT NOT NULL,
+ input_hash TEXT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ instruction_path TEXT NOT NULL,
+ program_id TEXT NOT NULL,
+ surface_code TEXT,
+ entry_code TEXT,
+ discriminator_hex TEXT,
+ status TEXT NOT NULL,
+ recognized BOOLEAN NOT NULL,
+ decoded_count INTEGER NOT NULL DEFAULT 0,
+ materialized_count INTEGER NOT NULL DEFAULT 0,
+ error_count INTEGER NOT NULL DEFAULT 0,
+ transaction_failed BOOLEAN NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_events.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_events.sql
new file mode 100644
index 0000000..39775f8
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_events.sql
@@ -0,0 +1,35 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_events.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_decode_events table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_decode_events (
+ id BIGSERIAL,
+ processor_name TEXT NOT NULL,
+ processor_version TEXT NOT NULL,
+ input_key TEXT NOT NULL,
+ input_hash TEXT NOT NULL,
+ event_key TEXT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ instruction_path TEXT NOT NULL,
+ program_id TEXT NOT NULL,
+ protocol_code TEXT NOT NULL,
+ surface_code TEXT NOT NULL,
+ event_code TEXT NOT NULL,
+ event_name TEXT NOT NULL,
+ event_family TEXT NOT NULL,
+ source_kind TEXT NOT NULL,
+ confidence TEXT NOT NULL,
+ proof_kind TEXT NOT NULL,
+ proof_jsonb JSONB NOT NULL,
+ payload_jsonb JSONB NOT NULL,
+ schema_kind TEXT,
+ schema_id TEXT,
+ schema_version TEXT,
+ schema_hash TEXT,
+ transaction_failed BOOLEAN NOT NULL,
+ transaction_error_jsonb JSONB,
+ observation_committed BOOLEAN NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_mat_outputs.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_mat_outputs.sql
new file mode 100644
index 0000000..22581ad
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_mat_outputs.sql
@@ -0,0 +1,22 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_mat_outputs.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_mat_outputs table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_mat_outputs (
+ id BIGSERIAL,
+ processor_name TEXT NOT NULL,
+ processor_version TEXT NOT NULL,
+ input_key TEXT NOT NULL,
+ input_hash TEXT NOT NULL,
+ output_key TEXT NOT NULL,
+ source_event_key TEXT NOT NULL,
+ source_decoder_name TEXT NOT NULL,
+ source_decoder_version TEXT NOT NULL,
+ source_decode_input_key TEXT NOT NULL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ materialized_family TEXT NOT NULL,
+ payload_jsonb JSONB NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_account_observations.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_account_observations.sql
new file mode 100644
index 0000000..74b819e
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_account_observations.sql
@@ -0,0 +1,34 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_account_observations.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_obs_account_observations table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_obs_account_observations (
+ id BIGSERIAL,
+ observation_key TEXT NOT NULL,
+ account_key TEXT NOT NULL,
+ context_slot BIGINT NOT NULL,
+ owner TEXT,
+ lamports NUMERIC(20,0),
+ executable BOOLEAN,
+ rent_epoch NUMERIC(20,0),
+ space BIGINT,
+ data_base64 TEXT,
+ data_hash TEXT,
+ provider TEXT NOT NULL,
+ endpoint_code TEXT,
+ protocol TEXT NOT NULL,
+ acquisition_method TEXT NOT NULL,
+ commitment TEXT,
+ capture_session_id TEXT,
+ filter_code TEXT,
+ origin TEXT NOT NULL,
+ detected_at TIMESTAMPTZ,
+ received_at TIMESTAMPTZ NOT NULL,
+ normalized_at TIMESTAMPTZ,
+ persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ payload_size_bytes BIGINT,
+ source_payload_hash TEXT,
+ status TEXT NOT NULL DEFAULT 'received',
+ error_code TEXT,
+ error_message TEXT
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_transaction_observations.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_transaction_observations.sql
new file mode 100644
index 0000000..9e92ef7
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_transaction_observations.sql
@@ -0,0 +1,28 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_transaction_observations.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_obs_transaction_observations table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_obs_transaction_observations (
+ id BIGSERIAL,
+ raw_transaction_id BIGINT,
+ observation_key TEXT NOT NULL,
+ signature TEXT,
+ slot BIGINT,
+ provider TEXT NOT NULL,
+ endpoint_code TEXT,
+ protocol TEXT NOT NULL,
+ acquisition_method TEXT NOT NULL,
+ origin TEXT NOT NULL,
+ commitment TEXT,
+ capture_session_id TEXT,
+ filter_code TEXT,
+ detected_at TIMESTAMPTZ,
+ received_at TIMESTAMPTZ NOT NULL,
+ normalized_at TIMESTAMPTZ,
+ persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ payload_size_bytes BIGINT,
+ source_payload_hash TEXT,
+ status TEXT NOT NULL DEFAULT 'received',
+ error_code TEXT,
+ error_message TEXT
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_ops_processing_ledger.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_ops_processing_ledger.sql
new file mode 100644
index 0000000..0c8aa1c
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_ops_processing_ledger.sql
@@ -0,0 +1,20 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_ops_processing_ledger.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_ops_processing_ledger table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_ops_processing_ledger (
+ id BIGSERIAL,
+ stage TEXT NOT NULL,
+ processor_name TEXT NOT NULL,
+ processor_version TEXT NOT NULL,
+ input_key TEXT NOT NULL,
+ input_hash TEXT NOT NULL,
+ status TEXT NOT NULL,
+ attempt_count INTEGER NOT NULL DEFAULT 0,
+ started_at TIMESTAMPTZ,
+ finished_at TIMESTAMPTZ,
+ error_code TEXT,
+ error_message TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql
new file mode 100644
index 0000000..2317003
--- /dev/null
+++ b/ks-store/migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql
@@ -0,0 +1,18 @@
+-- file: migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql
+-- version: 1
+
+-- Create the PostgreSQL k_sol_raw_transactions table when absent.
+CREATE TABLE IF NOT EXISTS k_sol_raw_transactions (
+ id BIGSERIAL,
+ signature TEXT NOT NULL,
+ slot BIGINT NOT NULL,
+ block_time BIGINT,
+ canonical_json JSONB,
+ canonical_json_hash TEXT,
+ canonical_format_version INTEGER NOT NULL DEFAULT 1,
+ retention_state TEXT NOT NULL DEFAULT 'full',
+ processing_state TEXT NOT NULL DEFAULT 'received',
+ lifecycle_reason TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
diff --git a/ks-store/src/constants.rs b/ks-store/src/constants.rs
index f8d0b43..615231d 100644
--- a/ks-store/src/constants.rs
+++ b/ks-store/src/constants.rs
@@ -1,9 +1,9 @@
// file: ks-store/src/constants.rs
-// version: 5
+// version: 6
//! Local constants for the `ks-store` crate.
/// Canonical tracing target for backend-independent storage operations.
pub(crate) const TRACING_TARGET: &str = "ks-store";
-/// Transitional schema contract identifier used until the `pre.003` baseline rebuild.
-pub const STORE_SCHEMA_CONTRACT_VERSION: &str = "0.5.3-pre.2-legacy-schema";
+/// Candidate N1-N3 store schema contract identifier established during `0.5.3`.
+pub const STORE_SCHEMA_CONTRACT_VERSION: &str = "0.5.3";
diff --git a/ks-store/src/contracts.rs b/ks-store/src/contracts.rs
index 85bcf6f..e7ca5f0 100644
--- a/ks-store/src/contracts.rs
+++ b/ks-store/src/contracts.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts.rs
-// version: 6
+// version: 7
//! Backend-neutral storage contracts used by pipeline crates.
@@ -11,10 +11,18 @@ mod pagination;
mod replay;
mod repository;
+/// Generic account acquisition observation insert contract.
+pub use self::dto::AccountObservationInsert;
+/// Transaction acquisition observation origin.
+pub use self::dto::AcquisitionObservationOrigin;
+/// Generic account acquisition observation status.
+pub use self::dto::AcquisitionObservationStatus;
/// Core account key insert contract.
pub use self::dto::CoreAccountKeyInsert;
/// Core account key source category.
pub use self::dto::CoreAccountKeySource;
+/// Core account-state insert contract.
+pub use self::dto::CoreAccountStateInsert;
/// Core balance change insert contract.
pub use self::dto::CoreBalanceChangeInsert;
/// Core balance change kind.
@@ -37,6 +45,8 @@ pub use self::dto::CoreInstructionProcessingState;
pub use self::dto::CoreInstructionReplayFilter;
/// Core log insert contract.
pub use self::dto::CoreLogInsert;
+/// Core return-data insert contract.
+pub use self::dto::CoreReturnDataInsert;
/// Core transaction insert contract.
pub use self::dto::CoreTransactionInsert;
/// One machine-readable decoder coverage declaration row.
@@ -51,20 +61,21 @@ pub use self::dto::DecodeFailure;
pub use self::dto::DecodeObservationInsert;
/// Atomic persistence bundle for one decoder and one contextual input.
pub use self::dto::DecodePersistenceBundle;
+pub use self::dto::DecodeSchemaProvenance;
/// Bounded contextual instruction selection filter for decode campaigns.
pub use self::dto::DecodeSelectionFilter;
/// Insert or upsert result contract returned by repositories.
pub use self::dto::InsertOutcome;
/// Maximum number of materialized rows returned by one bounded query.
-pub use self::dto::MAX_MATERIALIZED_EVENT_QUERY_ROWS;
+pub use self::dto::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS;
/// Atomic persistence bundle for one materializer and one decoded observation.
pub use self::dto::MaterializationPersistenceBundle;
-/// Bounded read-only materialized event selection.
-pub use self::dto::MaterializedEventFilter;
-/// One materialized output returned by a bounded query.
-pub use self::dto::MaterializedEventQueryRow;
+/// Bounded read-only materialized output selection.
+pub use self::dto::MaterializedOutputFilter;
/// One processor-owned materialized output row.
pub use self::dto::MaterializedOutputInsert;
+/// One materialized output returned by a bounded query.
+pub use self::dto::MaterializedOutputQueryRow;
/// Stable processing ledger identity.
pub use self::dto::ProcessingLedgerIdentity;
/// Stable processing ledger status.
@@ -105,12 +116,13 @@ pub use self::dto::StoreResourceStatistics;
pub use self::dto::StoreRuntimeSummary;
/// Transaction acquisition observation insert contract.
pub use self::dto::TransactionObservationInsert;
-/// Transaction acquisition observation origin.
-pub use self::dto::TransactionObservationOrigin;
/// Transaction acquisition observation status.
-pub use self::dto::TransactionObservationStatus;
+/// Generic account acquisition observation SQL-like row contract.
+pub use self::entity::AccountObservationRow;
/// Core account key SQL-like row contract.
pub use self::entity::CoreAccountKeyRow;
+/// Core account-state SQL-like row contract.
+pub use self::entity::CoreAccountStateRow;
/// Core balance change SQL-like row contract.
pub use self::entity::CoreBalanceChangeRow;
/// Core inner instruction SQL-like row contract.
@@ -119,6 +131,8 @@ pub use self::entity::CoreInnerInstructionRow;
pub use self::entity::CoreInstructionRow;
/// Core log SQL-like row contract.
pub use self::entity::CoreLogRow;
+/// Core return-data SQL-like row contract.
+pub use self::entity::CoreReturnDataRow;
/// Core transaction SQL-like row contract.
pub use self::entity::CoreTransactionRow;
/// Canonical raw Solana transaction SQL-like row contract.
diff --git a/ks-store/src/contracts/dto.rs b/ks-store/src/contracts/dto.rs
index 16d4642..cbbe0e9 100644
--- a/ks-store/src/contracts/dto.rs
+++ b/ks-store/src/contracts/dto.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/dto.rs
-// version: 6
+// version: 7
//! Backend-neutral DTO exports for storage repository contracts.
@@ -14,6 +14,8 @@ mod store;
pub use self::core::CoreAccountKeyInsert;
/// Core account key source category.
pub use self::core::CoreAccountKeySource;
+/// Core account-state insert contract.
+pub use self::core::CoreAccountStateInsert;
/// Core balance change insert contract.
pub use self::core::CoreBalanceChangeInsert;
/// Core balance change kind.
@@ -30,6 +32,8 @@ pub use self::core::CoreInstructionProcessingState;
pub use self::core::CoreInstructionReplayFilter;
/// Core log insert contract.
pub use self::core::CoreLogInsert;
+/// Core return-data insert contract.
+pub use self::core::CoreReturnDataInsert;
/// Core transaction insert contract.
pub use self::core::CoreTransactionInsert;
/// Complete normalized core extraction write bundle.
@@ -54,20 +58,27 @@ pub use self::decode::DecodeFailure;
pub use self::decode::DecodeObservationInsert;
/// Atomic persistence bundle for one decoder and one contextual input.
pub use self::decode::DecodePersistenceBundle;
+pub use self::decode::DecodeSchemaProvenance;
/// Bounded contextual instruction selection filter for decode campaigns.
pub use self::decode::DecodeSelectionFilter;
/// Maximum number of materialized rows returned by one bounded query.
-pub use self::decode::MAX_MATERIALIZED_EVENT_QUERY_ROWS;
+pub use self::decode::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS;
/// Atomic persistence bundle for one materializer and one decoded observation.
pub use self::decode::MaterializationPersistenceBundle;
-/// Bounded read-only materialized event selection.
-pub use self::decode::MaterializedEventFilter;
-/// One materialized output returned by a bounded query.
-pub use self::decode::MaterializedEventQueryRow;
+/// Bounded read-only materialized output selection.
+pub use self::decode::MaterializedOutputFilter;
/// One processor-owned materialized output row.
pub use self::decode::MaterializedOutputInsert;
+/// One materialized output returned by a bounded query.
+pub use self::decode::MaterializedOutputQueryRow;
/// Insert or upsert result contract returned by repositories.
pub use self::outcome::InsertOutcome;
+/// Generic account acquisition observation insert contract.
+pub use self::raw::AccountObservationInsert;
+/// Transaction acquisition observation origin.
+pub use self::raw::AcquisitionObservationOrigin;
+/// Generic account acquisition observation status.
+pub use self::raw::AcquisitionObservationStatus;
/// Raw payload lifecycle mark request.
pub use self::raw::RawPayloadLifecycleMark;
/// Raw payload processing state.
@@ -78,10 +89,7 @@ pub use self::raw::RawPayloadRetentionState;
pub use self::raw::RawTransactionInsert;
/// Transaction acquisition observation insert contract.
pub use self::raw::TransactionObservationInsert;
-/// Transaction acquisition observation origin.
-pub use self::raw::TransactionObservationOrigin;
/// Transaction acquisition observation status.
-pub use self::raw::TransactionObservationStatus;
/// Store backend diagnostic contract.
pub use self::store::StoreBackendDescriptor;
/// Backend-neutral diagnostic snapshot.
diff --git a/ks-store/src/contracts/dto/core.rs b/ks-store/src/contracts/dto/core.rs
index abf1d89..cfa0166 100644
--- a/ks-store/src/contracts/dto/core.rs
+++ b/ks-store/src/contracts/dto/core.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/dto/core.rs
-// version: 2
+// version: 3
//! Core Solana storage DTOs.
@@ -47,6 +47,8 @@ pub struct CoreTransactionInsert {
pub signature: std::string::String,
/// Transaction slot in Solana unsigned representation.
pub slot: u64,
+ /// Optional block timestamp as Unix seconds supplied by Solana.
+ pub block_time: std::option::Option,
/// Whether the transaction failed on-chain.
pub failed: bool,
/// Optional raw error JSON extracted from transaction metadata.
@@ -55,11 +57,12 @@ pub struct CoreTransactionInsert {
pub raw_transaction_id: std::option::Option,
}
-impl CoreTransactionInsert {
+impl crate::CoreTransactionInsert {
/// Builds a core transaction insert contract after minimal validation.
pub fn new(
signature: impl std::convert::Into,
slot: u64,
+ block_time: std::option::Option,
failed: bool,
err_json: std::option::Option,
) -> ks_core::Result {
@@ -74,6 +77,7 @@ impl CoreTransactionInsert {
return std::result::Result::Ok(Self {
signature: signature_value,
slot,
+ block_time,
failed,
err_json,
raw_transaction_id: std::option::Option::None,
@@ -99,7 +103,7 @@ pub struct CoreAccountKeyInsert {
/// Account public key as non-empty base58 text.
pub account_key: std::string::String,
/// Source category for this account key.
- pub source: CoreAccountKeySource,
+ pub source: crate::CoreAccountKeySource,
/// Whether the resolved account is writable for the transaction.
pub writable: bool,
/// Whether the resolved account signed the transaction.
@@ -108,7 +112,7 @@ pub struct CoreAccountKeyInsert {
pub executable: std::option::Option,
}
-impl CoreAccountKeyInsert {
+impl crate::CoreAccountKeyInsert {
/// Builds a core account key insert contract after minimal validation.
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -116,7 +120,7 @@ impl CoreAccountKeyInsert {
slot: u64,
account_index: u32,
account_key: impl std::convert::Into,
- source: CoreAccountKeySource,
+ source: crate::CoreAccountKeySource,
writable: bool,
signer: bool,
executable: std::option::Option,
@@ -148,6 +152,49 @@ impl CoreAccountKeyInsert {
}
}
+/// Canonical Core account-state observation produced from a raw account observation.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CoreAccountStateInsert {
+ /// Source raw account observation technical id.
+ pub source_observation_id: i64,
+ /// Observed account public key.
+ pub account_key: std::string::String,
+ /// Context slot of the normalized account state.
+ pub slot: u64,
+ /// Owning program id.
+ pub owner: std::string::String,
+ /// Exact lamports value.
+ pub lamports: u64,
+ /// Whether the account is executable.
+ pub executable: bool,
+ /// Exact rent epoch.
+ pub rent_epoch: u64,
+ /// Complete account-data length.
+ pub space: u64,
+ /// Exact account bytes encoded as standard base64.
+ pub data_base64: std::string::String,
+ /// Deterministic digest of decoded account bytes.
+ pub data_hash: std::string::String,
+}
+
+impl crate::CoreAccountStateInsert {
+ /// Validates a complete source-independent account state before persistence.
+ pub fn validate(&self) -> ks_core::Result<()> {
+ if self.source_observation_id <= 0 {
+ return std::result::Result::Err(ks_core::Error::db(
+ "core account state source observation id must be positive",
+ ));
+ }
+ let required = [self.account_key.as_str(), self.owner.as_str(), self.data_hash.as_str()];
+ if required.iter().any(|value| return value.trim().is_empty()) {
+ return std::result::Result::Err(ks_core::Error::db(
+ "core account state identity fields must not be empty",
+ ));
+ }
+ return std::result::Result::Ok(());
+ }
+}
+
/// Core instruction insert contract.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoreInstructionInsert {
@@ -159,6 +206,8 @@ pub struct CoreInstructionInsert {
pub instruction_path: std::string::String,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
+ /// Optional runtime invocation stack height.
+ pub stack_height: std::option::Option,
/// Instruction accounts as JSON, preserving unresolved forms when needed.
pub accounts_json: serde_json::Value,
/// Instruction payload JSON, preserving raw and partially decoded forms when needed.
@@ -166,16 +215,17 @@ pub struct CoreInstructionInsert {
/// Optional deterministic payload JSON hash.
pub payload_json_hash: std::option::Option,
/// Initial processing state used by replay schedulers.
- pub processing_state: CoreInstructionProcessingState,
+ pub processing_state: crate::CoreInstructionProcessingState,
}
-impl CoreInstructionInsert {
+impl crate::CoreInstructionInsert {
/// Builds a core instruction insert contract after minimal validation.
pub fn new(
signature: impl std::convert::Into,
slot: u64,
instruction_path: impl std::convert::Into,
program_id: impl std::convert::Into,
+ stack_height: std::option::Option,
accounts_json: serde_json::Value,
payload_json: serde_json::Value,
) -> ks_core::Result {
@@ -191,15 +241,21 @@ impl CoreInstructionInsert {
if let std::result::Result::Err(error) = validation_result {
return std::result::Result::Err(error);
}
+ if stack_height.is_some_and(|value| return value == 0) {
+ return std::result::Result::Err(ks_core::Error::db(
+ "core instruction stack height must be greater than zero when present",
+ ));
+ }
return std::result::Result::Ok(Self {
signature: signature_value,
slot,
instruction_path: instruction_path_value,
program_id: program_id_value,
+ stack_height,
accounts_json,
payload_json,
payload_json_hash: std::option::Option::None,
- processing_state: CoreInstructionProcessingState::Pending,
+ processing_state: crate::CoreInstructionProcessingState::Pending,
});
}
@@ -232,15 +288,19 @@ pub struct CoreInnerInstructionInsert {
pub instruction_path: std::string::String,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
+ /// Optional runtime invocation stack height.
+ pub stack_height: std::option::Option,
/// Inner instruction accounts as JSON.
pub accounts_json: serde_json::Value,
/// Inner instruction payload JSON.
pub payload_json: serde_json::Value,
/// Optional deterministic payload JSON hash.
pub payload_json_hash: std::option::Option,
+ /// Initial processing state used by CPI replay schedulers.
+ pub processing_state: crate::CoreInstructionProcessingState,
}
-impl CoreInnerInstructionInsert {
+impl crate::CoreInnerInstructionInsert {
/// Builds a core inner instruction insert contract after minimal validation.
pub fn new(
signature: impl std::convert::Into,
@@ -248,6 +308,7 @@ impl CoreInnerInstructionInsert {
parent_instruction_path: impl std::convert::Into,
instruction_path: impl std::convert::Into,
program_id: impl std::convert::Into,
+ stack_height: std::option::Option,
accounts_json: serde_json::Value,
payload_json: serde_json::Value,
) -> ks_core::Result {
@@ -271,15 +332,22 @@ impl CoreInnerInstructionInsert {
if let std::result::Result::Err(error) = validation_result {
return std::result::Result::Err(error);
}
+ if stack_height.is_some_and(|value| return value <= 1) {
+ return std::result::Result::Err(ks_core::Error::db(
+ "core inner instruction stack height must be greater than one when present",
+ ));
+ }
return std::result::Result::Ok(Self {
signature: signature_value,
slot,
parent_instruction_path: parent_instruction_path_value,
instruction_path: instruction_path_value,
program_id: program_id_value,
+ stack_height,
accounts_json,
payload_json,
payload_json_hash: std::option::Option::None,
+ processing_state: crate::CoreInstructionProcessingState::Pending,
});
}
@@ -299,6 +367,52 @@ impl CoreInnerInstructionInsert {
}
}
+/// Transaction return-data fact retained in Core for future decoders.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CoreReturnDataInsert {
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Transaction slot.
+ pub slot: u64,
+ /// Program id that produced the return data.
+ pub program_id: std::string::String,
+ /// Exact return bytes encoded as standard base64.
+ pub data_base64: std::string::String,
+}
+
+impl crate::CoreReturnDataInsert {
+ /// Builds a validated Core return-data fact.
+ pub fn new(
+ signature: impl std::convert::Into,
+ slot: u64,
+ program_id: impl std::convert::Into,
+ data_base64: impl std::convert::Into,
+ ) -> ks_core::Result {
+ let signature_value = signature.into();
+ let program_id_value = program_id.into();
+ let signature_result = validate_required_text(
+ &signature_value,
+ "core return-data signature must not be empty",
+ );
+ if let std::result::Result::Err(error) = signature_result {
+ return std::result::Result::Err(error);
+ }
+ let program_result = validate_required_text(
+ &program_id_value,
+ "core return-data program id must not be empty",
+ );
+ if let std::result::Result::Err(error) = program_result {
+ return std::result::Result::Err(error);
+ }
+ return std::result::Result::Ok(Self {
+ signature: signature_value,
+ slot,
+ program_id: program_id_value,
+ data_base64: data_base64.into(),
+ });
+ }
+}
+
/// Core log insert contract.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoreLogInsert {
@@ -318,7 +432,7 @@ pub struct CoreLogInsert {
pub log_text_hash: std::option::Option,
}
-impl CoreLogInsert {
+impl crate::CoreLogInsert {
/// Builds a core log insert contract after minimal validation.
pub fn new(
signature: impl std::convert::Into,
@@ -391,7 +505,7 @@ pub struct CoreBalanceChangeInsert {
/// Stable balance change index preserving extraction order.
pub balance_change_index: u32,
/// Balance change family.
- pub balance_kind: CoreBalanceChangeKind,
+ pub balance_kind: crate::CoreBalanceChangeKind,
/// Optional account index when available.
pub account_index: std::option::Option,
/// Optional account public key when available.
@@ -408,14 +522,14 @@ pub struct CoreBalanceChangeInsert {
pub delta_json: std::option::Option,
}
-impl CoreBalanceChangeInsert {
+impl crate::CoreBalanceChangeInsert {
/// Builds a core balance change insert contract after minimal validation.
#[allow(clippy::too_many_arguments)]
pub fn new(
signature: impl std::convert::Into,
slot: u64,
balance_change_index: u32,
- balance_kind: CoreBalanceChangeKind,
+ balance_kind: crate::CoreBalanceChangeKind,
account_index: std::option::Option,
account_key: std::option::Option,
mint: std::option::Option,
@@ -473,7 +587,7 @@ impl CoreBalanceChangeInsert {
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoreInstructionReplayFilter {
/// Optional processing state to select, usually `Pending`, `Failed` or `ReplayRequested`.
- pub processing_state: std::option::Option,
+ pub processing_state: std::option::Option,
/// Optional program id filter.
pub program_id: std::option::Option,
/// Optional inclusive minimum slot.
@@ -482,10 +596,10 @@ pub struct CoreInstructionReplayFilter {
pub max_slot: std::option::Option,
}
-impl CoreInstructionReplayFilter {
+impl crate::CoreInstructionReplayFilter {
/// Builds a replay filter after minimal validation.
pub fn new(
- processing_state: std::option::Option,
+ processing_state: std::option::Option,
program_id: std::option::Option,
min_slot: std::option::Option,
max_slot: std::option::Option,
@@ -519,7 +633,9 @@ impl CoreInstructionReplayFilter {
/// Builds the default pending instruction replay filter.
pub fn pending() -> Self {
return Self {
- processing_state: std::option::Option::Some(CoreInstructionProcessingState::Pending),
+ processing_state: std::option::Option::Some(
+ crate::CoreInstructionProcessingState::Pending,
+ ),
program_id: std::option::Option::None,
min_slot: std::option::Option::None,
max_slot: std::option::Option::None,
@@ -535,7 +651,7 @@ pub struct CoreInstructionLifecycleMark {
/// Stable instruction path, for example `0` or `2/1`.
pub instruction_path: std::string::String,
/// New processing state.
- pub processing_state: CoreInstructionProcessingState,
+ pub processing_state: crate::CoreInstructionProcessingState,
/// Optional processor name that produced the lifecycle transition.
pub processor_name: std::option::Option,
/// Optional processor version that produced the lifecycle transition.
@@ -544,12 +660,12 @@ pub struct CoreInstructionLifecycleMark {
pub reason: std::option::Option,
}
-impl CoreInstructionLifecycleMark {
+impl crate::CoreInstructionLifecycleMark {
/// Builds a core instruction lifecycle mark after minimal validation.
pub fn new(
signature: impl std::convert::Into,
instruction_path: impl std::convert::Into,
- processing_state: CoreInstructionProcessingState,
+ processing_state: crate::CoreInstructionProcessingState,
processor_name: std::option::Option,
processor_version: std::option::Option,
reason: std::option::Option,
@@ -658,13 +774,25 @@ fn validate_optional_text(
mod tests {
#[test]
fn core_transaction_rejects_empty_signature() {
- let result = crate::CoreTransactionInsert::new(" ", 1, false, std::option::Option::None);
+ let result = crate::CoreTransactionInsert::new(
+ " ",
+ 1,
+ std::option::Option::None,
+ false,
+ std::option::Option::None,
+ );
assert!(result.is_err());
}
#[test]
fn core_transaction_accepts_optional_raw_lineage() {
- let result = crate::CoreTransactionInsert::new("abc", 1, false, std::option::Option::None);
+ let result = crate::CoreTransactionInsert::new(
+ "abc",
+ 1,
+ std::option::Option::None,
+ false,
+ std::option::Option::None,
+ );
let input = match result {
std::result::Result::Ok(value) => value.with_raw_transaction_id(7),
std::result::Result::Err(error) => panic!("unexpected transaction error: {error}"),
@@ -694,6 +822,7 @@ mod tests {
1,
"0",
" ",
+ std::option::Option::None,
serde_json::json!([]),
serde_json::json!({}),
);
@@ -707,6 +836,7 @@ mod tests {
1,
"0",
"program",
+ std::option::Option::None,
serde_json::json!([]),
serde_json::json!({}),
);
@@ -725,6 +855,36 @@ mod tests {
" ",
"0/0",
"program",
+ std::option::Option::None,
+ serde_json::json!([]),
+ serde_json::json!({}),
+ );
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn core_instruction_rejects_zero_stack_height() {
+ let result = crate::CoreInstructionInsert::new(
+ "abc",
+ 1,
+ "0",
+ "program",
+ std::option::Option::Some(0),
+ serde_json::json!([]),
+ serde_json::json!({}),
+ );
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn core_inner_instruction_rejects_top_level_stack_height() {
+ let result = crate::CoreInnerInstructionInsert::new(
+ "abc",
+ 1,
+ "0",
+ "0/0",
+ "program",
+ std::option::Option::Some(1),
serde_json::json!([]),
serde_json::json!({}),
);
@@ -762,6 +922,23 @@ mod tests {
assert!(result.is_err());
}
+ #[test]
+ fn core_account_state_requires_positive_lineage() {
+ let state = crate::CoreAccountStateInsert {
+ source_observation_id: 0,
+ account_key: "11111111111111111111111111111111".to_string(),
+ slot: 1,
+ owner: "11111111111111111111111111111111".to_string(),
+ lamports: 0,
+ executable: false,
+ rent_epoch: 0,
+ space: 0,
+ data_base64: std::string::String::new(),
+ data_hash: "hash".to_string(),
+ };
+ assert!(state.validate().is_err());
+ }
+
#[test]
fn replay_input_rejects_empty_key() {
let result = crate::MdCoreInstructionReplayInput::new(
@@ -785,7 +962,7 @@ mod tests {
}
#[test]
- fn replay_input_accepts_ordered_outer_instruction_array() {
+ fn replay_input_accepts_ordered_top_level_instruction_array() {
let result = crate::MdCoreInstructionReplayInput::new(
"abc:2",
"abc",
@@ -822,15 +999,15 @@ mod tests {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"),
};
- assert_eq!(input.core_contract_version, 2);
+ assert_eq!(input.core_contract_version, crate::MD_CORE_REPLAY_INPUT_CONTRACT_VERSION);
assert_eq!(
- input.outer_instructions_json.as_array().map(std::vec::Vec::len),
+ input.top_level_instructions_json.as_array().map(std::vec::Vec::len),
std::option::Option::Some(2)
);
}
#[test]
- fn replay_input_rejects_non_array_outer_instruction_context() {
+ fn replay_input_rejects_non_array_top_level_instruction_context() {
let result = crate::MdCoreInstructionReplayInput::new(
"abc:0",
"abc",
diff --git a/ks-store/src/contracts/dto/core_extraction.rs b/ks-store/src/contracts/dto/core_extraction.rs
index bf326ca..7f74c6a 100644
--- a/ks-store/src/contracts/dto/core_extraction.rs
+++ b/ks-store/src/contracts/dto/core_extraction.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/dto/core_extraction.rs
-// version: 2
+// version: 3
//! Canonical transaction to core extraction storage contracts.
@@ -31,7 +31,7 @@ pub struct CoreExtractionSelectionFilter {
pub limit: u32,
}
-impl CoreExtractionSelectionFilter {
+impl crate::CoreExtractionSelectionFilter {
/// Builds a validated extraction selection filter.
pub fn new(
signatures: std::vec::Vec,
@@ -105,7 +105,7 @@ pub struct ProcessingLedgerIdentity {
pub input_hash: std::string::String,
}
-impl ProcessingLedgerIdentity {
+impl crate::ProcessingLedgerIdentity {
/// Builds a validated processing ledger identity.
pub fn new(
stage: impl std::convert::Into,
@@ -154,9 +154,11 @@ pub struct CoreExtractionBundle {
pub logs: std::vec::Vec,
/// Native and token balance changes.
pub balance_changes: std::vec::Vec,
+ /// Optional transaction return data retained by Core.
+ pub return_data: std::option::Option,
}
-impl CoreExtractionBundle {
+impl crate::CoreExtractionBundle {
/// Validates lineage and stable signature consistency across the bundle.
pub fn validate(&self) -> ks_core::Result<()> {
if self.raw_transaction_id <= 0 {
@@ -211,6 +213,13 @@ impl CoreExtractionBundle {
));
}
}
+ if let std::option::Option::Some(return_data) = &self.return_data {
+ if return_data.signature != signature {
+ return std::result::Result::Err(ks_core::Error::db(
+ "core extraction return-data signature mismatch",
+ ));
+ }
+ }
return std::result::Result::Ok(());
}
}
@@ -228,7 +237,7 @@ pub struct CoreExtractionFailure {
pub error_message: std::string::String,
}
-impl CoreExtractionFailure {
+impl crate::CoreExtractionFailure {
/// Builds a validated failure record.
pub fn new(
raw_transaction_id: i64,
diff --git a/ks-store/src/contracts/dto/decode.rs b/ks-store/src/contracts/dto/decode.rs
index 156edf8..14e533c 100644
--- a/ks-store/src/contracts/dto/decode.rs
+++ b/ks-store/src/contracts/dto/decode.rs
@@ -1,14 +1,14 @@
// file: ks-store/src/contracts/dto/decode.rs
-// version: 5
+// version: 6
//! Backend-neutral decode, coverage and materialization persistence DTOs.
/// Maximum number of materialized rows returned by one bounded query.
-pub const MAX_MATERIALIZED_EVENT_QUERY_ROWS: u32 = 500;
+pub const MAX_MATERIALIZED_OUTPUT_QUERY_ROWS: u32 = 500;
-/// Bounded read-only materialized event selection.
+/// Bounded read-only materialized output selection.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
-pub struct MaterializedEventFilter {
+pub struct MaterializedOutputFilter {
/// Optional exact materializer processor name.
pub processor_name: std::option::Option,
/// Optional exact materialized family code.
@@ -19,18 +19,18 @@ pub struct MaterializedEventFilter {
pub limit: u32,
}
-impl MaterializedEventFilter {
- /// Builds and validates a bounded materialized event filter.
+impl crate::MaterializedOutputFilter {
+ /// Builds and validates a bounded materialized output filter.
pub fn new(
processor_name: std::option::Option,
materialized_family: std::option::Option,
signature_contains: std::option::Option,
limit: u32,
) -> ks_core::Result {
- if limit == 0 || limit > crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS {
+ if limit == 0 || limit > crate::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS {
return std::result::Result::Err(ks_core::Error::db(format!(
- "materialized event query limit must be between 1 and {}",
- crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS
+ "materialized output query limit must be between 1 and {}",
+ crate::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS
)));
}
return std::result::Result::Ok(Self {
@@ -48,7 +48,7 @@ impl MaterializedEventFilter {
/// One materialized output returned by the common bounded query contract.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
-pub struct MaterializedEventQueryRow {
+pub struct MaterializedOutputQueryRow {
/// Materializer processor name.
pub processor_name: std::string::String,
/// Materializer processor version.
@@ -98,7 +98,7 @@ pub struct DecodeSelectionFilter {
pub limit: u32,
}
-impl DecodeSelectionFilter {
+impl crate::DecodeSelectionFilter {
/// Builds a validated bounded decode selection filter.
#[allow(clippy::too_many_arguments)]
pub fn new(
@@ -165,6 +165,38 @@ impl DecodeSelectionFilter {
}
}
+/// Optional external schema provenance used by a decoder for one observation.
+#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct DecodeSchemaProvenance {
+ /// Stable schema mechanism code, for example an IDL family or another external schema system.
+ pub schema_kind: std::string::String,
+ /// Stable schema identity within the selected mechanism.
+ pub schema_id: std::string::String,
+ /// Optional source schema version when one is published independently from the decoder.
+ pub schema_version: std::option::Option,
+ /// Deterministic schema content hash used to reproduce the decode.
+ pub schema_hash: std::string::String,
+}
+
+impl crate::DecodeSchemaProvenance {
+ /// Validates the source-neutral schema identity without assuming a specific IDL mechanism.
+ pub fn validate(&self) -> ks_core::Result<()> {
+ if self.schema_kind.trim().is_empty()
+ || self.schema_id.trim().is_empty()
+ || self.schema_hash.trim().is_empty()
+ || self
+ .schema_version
+ .as_deref()
+ .is_some_and(|value| return value.trim().is_empty())
+ {
+ return std::result::Result::Err(ks_core::Error::db(
+ "decode schema provenance fields must not be empty",
+ ));
+ }
+ return std::result::Result::Ok(());
+ }
+}
+
/// One processor-owned decoded observation row.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DecodeObservationInsert {
@@ -206,6 +238,8 @@ pub struct DecodeObservationInsert {
pub proof_json: serde_json::Value,
/// Typed decoded payload JSON.
pub payload_json: serde_json::Value,
+ /// Optional external schema provenance when decoding depends on a schema independent of decoder code.
+ pub schema_provenance: std::option::Option,
/// Whether the source transaction failed on-chain.
pub transaction_failed: bool,
/// Optional source transaction error JSON.
@@ -214,7 +248,7 @@ pub struct DecodeObservationInsert {
pub observation_committed: bool,
}
-impl DecodeObservationInsert {
+impl crate::DecodeObservationInsert {
/// Validates stable identities and failed transaction commit semantics.
pub fn validate(&self) -> ks_core::Result<()> {
let fields = [
@@ -240,6 +274,12 @@ impl DecodeObservationInsert {
"decoded observation identity fields must not be empty",
));
}
+ if let std::option::Option::Some(schema_provenance) = &self.schema_provenance {
+ let schema_result = schema_provenance.validate();
+ if let std::result::Result::Err(error) = schema_result {
+ return std::result::Result::Err(error);
+ }
+ }
if self.transaction_failed && self.observation_committed {
return std::result::Result::Err(ks_core::Error::db(
"failed transaction decoded observations must not be committed",
@@ -325,12 +365,12 @@ pub struct DecodePersistenceBundle {
/// Optional human-readable terminal error message.
pub error_message: std::option::Option,
/// Processor-owned decoded observations.
- pub observations: std::vec::Vec,
+ pub observations: std::vec::Vec,
/// Coverage observation for this attempt.
- pub coverage: DecodeCoverageObservationInsert,
+ pub coverage: crate::DecodeCoverageObservationInsert,
}
-impl DecodePersistenceBundle {
+impl crate::DecodePersistenceBundle {
/// Validates identities shared by every atomic decode output.
pub fn validate(&self) -> ks_core::Result<()> {
if self.ledger_identity.stage != "instruction_decode"
@@ -471,10 +511,10 @@ pub struct MaterializationPersistenceBundle {
/// Optional human-readable terminal error message.
pub error_message: std::option::Option,
/// Processor-owned materialized outputs.
- pub outputs: std::vec::Vec,
+ pub outputs: std::vec::Vec,
}
-impl MaterializationPersistenceBundle {
+impl crate::MaterializationPersistenceBundle {
/// Validates the materializer, source decoder and output identities.
pub fn validate(&self) -> ks_core::Result<()> {
if self.ledger_identity.stage != "event_materialization"
@@ -630,12 +670,12 @@ mod tests {
}
#[test]
- fn materialized_event_filter_is_bounded_and_trims_optional_text() {
- let result = crate::MaterializedEventFilter::new(
+ fn materialized_output_filter_is_bounded_and_trims_optional_text() {
+ let result = crate::MaterializedOutputFilter::new(
std::option::Option::Some(" materializer.transaction.annotations ".to_string()),
std::option::Option::Some(" transaction_annotation ".to_string()),
std::option::Option::Some(" signature ".to_string()),
- crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS,
+ crate::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS,
);
let filter = match result {
std::result::Result::Ok(value) => value,
@@ -647,7 +687,7 @@ mod tests {
);
assert_eq!(filter.signature_contains.as_deref(), std::option::Option::Some("signature"));
assert!(
- crate::MaterializedEventFilter::new(
+ crate::MaterializedOutputFilter::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
@@ -657,6 +697,24 @@ mod tests {
);
}
+ #[test]
+ fn decode_schema_provenance_requires_reproducible_identity() {
+ let valid = crate::DecodeSchemaProvenance {
+ schema_kind: "idl".to_string(),
+ schema_id: "program-schema".to_string(),
+ schema_version: std::option::Option::Some("1".to_string()),
+ schema_hash: "abc123".to_string(),
+ };
+ assert!(valid.validate().is_ok());
+ let invalid = crate::DecodeSchemaProvenance {
+ schema_kind: "idl".to_string(),
+ schema_id: " ".to_string(),
+ schema_version: std::option::Option::None,
+ schema_hash: "abc123".to_string(),
+ };
+ assert!(invalid.validate().is_err());
+ }
+
#[test]
fn failed_decoded_observation_cannot_be_committed() {
let input = crate::DecodeObservationInsert {
@@ -679,6 +737,7 @@ mod tests {
proof_kind: "exact_layout".to_string(),
proof_json: serde_json::json!({}),
payload_json: serde_json::json!({}),
+ schema_provenance: std::option::Option::None,
transaction_failed: true,
transaction_error: std::option::Option::Some(serde_json::json!({})),
observation_committed: true,
diff --git a/ks-store/src/contracts/dto/raw.rs b/ks-store/src/contracts/dto/raw.rs
index 21253a7..b839dd5 100644
--- a/ks-store/src/contracts/dto/raw.rs
+++ b/ks-store/src/contracts/dto/raw.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/dto/raw.rs
-// version: 2
+// version: 3
//! Canonical Solana transaction and acquisition observation storage DTOs.
@@ -31,35 +31,35 @@ pub enum RawPayloadProcessingState {
Failed,
}
-/// Origin of one transaction acquisition observation.
+/// Origin of one source acquisition observation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
-pub enum TransactionObservationOrigin {
- /// Transaction was observed from a current live stream.
+pub enum AcquisitionObservationOrigin {
+ /// Data was observed from a current live stream.
Live,
- /// Transaction was acquired by an explicit historical backfill.
+ /// Data was acquired by an explicit historical backfill.
Backfill,
- /// Transaction was replayed from an already captured source.
+ /// Data was replayed from an already captured source.
Replay,
- /// Transaction was fetched to repair an acquisition gap.
+ /// Data was fetched to repair an acquisition gap.
Repair,
/// Observation was converted from a historical storage table.
Migration,
}
-/// Technical status of one transaction acquisition observation.
+/// Technical status of one source acquisition observation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
-pub enum TransactionObservationStatus {
- /// A transaction candidate was detected before a complete payload was received.
+pub enum AcquisitionObservationStatus {
+ /// A source candidate was detected before a complete payload was received.
Detected,
/// A source payload was received.
Received,
- /// A source payload was normalized into the canonical transaction contract.
+ /// A source payload was normalized into its canonical N1 contract.
Normalized,
- /// The observation and any linked canonical transaction were persisted.
+ /// The observation and any linked canonical payload were persisted.
Persisted,
/// Acquisition or normalization failed.
Failed,
- /// The source reported or implied a transaction that was temporarily unavailable.
+ /// The source reported that the requested source object was unavailable.
Missing,
}
@@ -70,6 +70,8 @@ pub struct RawTransactionInsert {
pub signature: std::string::String,
/// Transaction slot in Solana unsigned representation.
pub slot: u64,
+ /// Optional block timestamp as Unix seconds supplied by Solana.
+ pub block_time: std::option::Option,
/// Canonical source-independent transaction document.
pub canonical_json: serde_json::Value,
/// Optional deterministic digest of the canonical document.
@@ -78,11 +80,12 @@ pub struct RawTransactionInsert {
pub canonical_format_version: u32,
}
-impl RawTransactionInsert {
+impl crate::RawTransactionInsert {
/// Builds a canonical raw transaction insert contract after minimal validation.
pub fn new(
signature: impl std::convert::Into,
slot: u64,
+ block_time: std::option::Option,
canonical_json: serde_json::Value,
canonical_format_version: u32,
) -> ks_core::Result {
@@ -100,6 +103,7 @@ impl RawTransactionInsert {
return std::result::Result::Ok(Self {
signature: signature_value,
slot,
+ block_time,
canonical_json,
canonical_json_hash: std::option::Option::None,
canonical_format_version,
@@ -121,6 +125,7 @@ impl RawTransactionInsert {
let insert_result = Self::new(
transaction.primary_signature.clone(),
transaction.slot,
+ transaction.block_time,
canonical_json,
transaction.format_version,
);
@@ -147,6 +152,115 @@ impl RawTransactionInsert {
}
}
+/// Generic Solana account acquisition observation retained for future account decoders.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct AccountObservationInsert {
+ /// Stable observation key used for idempotence.
+ pub observation_key: std::string::String,
+ /// Observed account public key.
+ pub account_key: std::string::String,
+ /// RPC or stream context slot associated with this observation.
+ pub context_slot: u64,
+ /// Account owner when the account exists.
+ pub owner: std::option::Option,
+ /// Account lamports when the account exists.
+ pub lamports: std::option::Option,
+ /// Executable flag when the account exists.
+ pub executable: std::option::Option,
+ /// Rent epoch when the account exists.
+ pub rent_epoch: std::option::Option,
+ /// Complete account-data size when known.
+ pub space: std::option::Option,
+ /// Exact account bytes encoded as standard base64 when captured.
+ pub data_base64: std::option::Option,
+ /// Deterministic digest of the decoded account bytes when captured.
+ pub data_hash: std::option::Option,
+ /// Acquisition provider code.
+ pub provider: std::string::String,
+ /// Optional configured endpoint code.
+ pub endpoint_code: std::option::Option,
+ /// Acquisition protocol.
+ pub protocol: std::string::String,
+ /// Acquisition method.
+ pub acquisition_method: std::string::String,
+ /// Optional commitment used for the read or subscription.
+ pub commitment: std::option::Option,
+ /// Optional capture session identifier.
+ pub capture_session_id: std::option::Option,
+ /// Optional configured filter code.
+ pub filter_code: std::option::Option,
+ /// Acquisition origin.
+ pub origin: crate::AcquisitionObservationOrigin,
+ /// Optional first detection timestamp.
+ pub detected_at: std::option::Option>,
+ /// Timestamp at which the observation was received locally.
+ pub received_at: chrono::DateTime,
+ /// Optional timestamp at which generic normalization completed.
+ pub normalized_at: std::option::Option>,
+ /// Optional uncompressed source payload size in bytes.
+ pub payload_size_bytes: std::option::Option,
+ /// Optional digest of the source-specific payload without retaining that payload.
+ pub source_payload_hash: std::option::Option,
+ /// Technical observation status.
+ pub status: crate::AcquisitionObservationStatus,
+ /// Optional stable error code.
+ pub error_code: std::option::Option,
+ /// Optional bounded diagnostic message.
+ pub error_message: std::option::Option,
+}
+
+impl crate::AccountObservationInsert {
+ /// Validates the replayable account observation shape without assuming a decoder schema.
+ pub fn validate(&self) -> ks_core::Result<()> {
+ let required_result = validate_required_observation_texts(
+ self.observation_key.as_str(),
+ self.provider.as_str(),
+ self.protocol.as_str(),
+ self.acquisition_method.as_str(),
+ );
+ if let std::result::Result::Err(error) = required_result {
+ return std::result::Result::Err(error);
+ }
+ if self.account_key.trim().is_empty() {
+ return std::result::Result::Err(ks_core::Error::db(
+ "account observation account key must not be empty",
+ ));
+ }
+ let optional_texts = [
+ self.owner.as_deref(),
+ self.endpoint_code.as_deref(),
+ self.commitment.as_deref(),
+ self.capture_session_id.as_deref(),
+ self.filter_code.as_deref(),
+ self.data_hash.as_deref(),
+ self.source_payload_hash.as_deref(),
+ self.error_code.as_deref(),
+ self.error_message.as_deref(),
+ ];
+ if optional_texts.iter().flatten().any(|value| return value.trim().is_empty()) {
+ return std::result::Result::Err(ks_core::Error::db(
+ "account observation optional text fields must not be empty when present",
+ ));
+ }
+ let state_fields_present = [
+ self.owner.is_some(),
+ self.lamports.is_some(),
+ self.executable.is_some(),
+ self.rent_epoch.is_some(),
+ self.space.is_some(),
+ self.data_base64.is_some(),
+ self.data_hash.is_some(),
+ ];
+ let present_count = state_fields_present.iter().filter(|present| return **present).count();
+ if present_count != 0 && present_count != state_fields_present.len() {
+ return std::result::Result::Err(ks_core::Error::db(
+ "account observation state fields must be complete or entirely absent",
+ ));
+ }
+ return std::result::Result::Ok(());
+ }
+}
+
/// Lightweight transaction acquisition observation insert contract.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct TransactionObservationInsert {
@@ -167,7 +281,7 @@ pub struct TransactionObservationInsert {
/// Source acquisition method.
pub acquisition_method: std::string::String,
/// Observation origin.
- pub origin: TransactionObservationOrigin,
+ pub origin: crate::AcquisitionObservationOrigin,
/// Optional Solana commitment.
pub commitment: std::option::Option,
/// Optional capture session identifier.
@@ -185,21 +299,21 @@ pub struct TransactionObservationInsert {
/// Optional digest of the source-specific payload without retaining that payload.
pub source_payload_hash: std::option::Option,
/// Observation status.
- pub status: TransactionObservationStatus,
+ pub status: crate::AcquisitionObservationStatus,
/// Optional machine-readable error code.
pub error_code: std::option::Option,
/// Optional diagnostic error message.
pub error_message: std::option::Option,
}
-impl TransactionObservationInsert {
+impl crate::TransactionObservationInsert {
/// Builds a lightweight transaction observation after validating required source metadata.
pub fn new(
observation_key: impl std::convert::Into,
provider: impl std::convert::Into,
protocol: impl std::convert::Into,
acquisition_method: impl std::convert::Into,
- origin: TransactionObservationOrigin,
+ origin: crate::AcquisitionObservationOrigin,
received_at: chrono::DateTime,
) -> ks_core::Result {
let observation_key_value = observation_key.into();
@@ -233,7 +347,7 @@ impl TransactionObservationInsert {
normalized_at: std::option::Option::None,
payload_size_bytes: std::option::Option::None,
source_payload_hash: std::option::Option::None,
- status: TransactionObservationStatus::Received,
+ status: crate::AcquisitionObservationStatus::Received,
error_code: std::option::Option::None,
error_message: std::option::Option::None,
});
@@ -356,7 +470,7 @@ impl TransactionObservationInsert {
}
/// Replaces the current observation status.
- pub fn with_status(mut self, status: TransactionObservationStatus) -> Self {
+ pub fn with_status(mut self, status: crate::AcquisitionObservationStatus) -> Self {
self.status = status;
return self;
}
@@ -384,7 +498,7 @@ impl TransactionObservationInsert {
}
self.error_code = std::option::Option::Some(error_code_value);
self.error_message = error_message;
- self.status = TransactionObservationStatus::Failed;
+ self.status = crate::AcquisitionObservationStatus::Failed;
return std::result::Result::Ok(self);
}
}
@@ -392,25 +506,25 @@ impl TransactionObservationInsert {
/// Canonical raw transaction lifecycle mark request.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct RawPayloadLifecycleMark {
- /// Physical canonical raw table name using the `kb_sol__` convention.
+ /// Physical canonical raw table name using the `k_sol__` convention.
pub raw_table_name: std::string::String,
/// Stable canonical raw row key, currently the transaction signature.
pub raw_row_key: std::string::String,
/// Retention state to record for the canonical payload.
- pub retention_state: RawPayloadRetentionState,
+ pub retention_state: crate::RawPayloadRetentionState,
/// Processing state to record for the canonical payload.
- pub processing_state: RawPayloadProcessingState,
+ pub processing_state: crate::RawPayloadProcessingState,
/// Optional reason visible in diagnostics.
pub reason: std::option::Option,
}
-impl RawPayloadLifecycleMark {
+impl crate::RawPayloadLifecycleMark {
/// Builds a canonical raw payload lifecycle mark after minimal validation.
pub fn new(
raw_table_name: impl std::convert::Into,
raw_row_key: impl std::convert::Into,
- retention_state: RawPayloadRetentionState,
- processing_state: RawPayloadProcessingState,
+ retention_state: crate::RawPayloadRetentionState,
+ processing_state: crate::RawPayloadProcessingState,
reason: std::option::Option,
) -> ks_core::Result {
let raw_table_name_value = raw_table_name.into();
@@ -488,13 +602,25 @@ fn validate_optional_owned_text(
mod tests {
#[test]
fn raw_transaction_rejects_empty_signature() {
- let result = crate::RawTransactionInsert::new(" ", 1, serde_json::json!({"ok": true}), 1);
+ let result = crate::RawTransactionInsert::new(
+ " ",
+ 1,
+ std::option::Option::None,
+ serde_json::json!({"ok": true}),
+ 1,
+ );
assert!(result.is_err());
}
#[test]
fn raw_transaction_rejects_zero_format_version() {
- let result = crate::RawTransactionInsert::new("abc", 1, serde_json::json!({"ok": true}), 0);
+ let result = crate::RawTransactionInsert::new(
+ "abc",
+ 1,
+ std::option::Option::None,
+ serde_json::json!({"ok": true}),
+ 0,
+ );
assert!(result.is_err());
}
@@ -565,10 +691,49 @@ mod tests {
#[test]
fn raw_transaction_accepts_canonical_payload() {
- let result = crate::RawTransactionInsert::new("abc", 1, serde_json::json!({"ok": true}), 1);
+ let result = crate::RawTransactionInsert::new(
+ "abc",
+ 1,
+ std::option::Option::None,
+ serde_json::json!({"ok": true}),
+ 1,
+ );
assert!(result.is_ok());
}
+ #[test]
+ fn account_observation_requires_complete_or_absent_state() {
+ let observation = crate::AccountObservationInsert {
+ observation_key: "account:1".to_string(),
+ account_key: "11111111111111111111111111111111".to_string(),
+ context_slot: 1,
+ owner: std::option::Option::Some("11111111111111111111111111111111".to_string()),
+ lamports: std::option::Option::None,
+ executable: std::option::Option::Some(false),
+ rent_epoch: std::option::Option::Some(0),
+ space: std::option::Option::Some(0),
+ data_base64: std::option::Option::Some(std::string::String::new()),
+ data_hash: std::option::Option::Some("hash".to_string()),
+ provider: "rpc".to_string(),
+ endpoint_code: std::option::Option::None,
+ protocol: "solana_http".to_string(),
+ acquisition_method: "getAccountInfo".to_string(),
+ commitment: std::option::Option::None,
+ capture_session_id: std::option::Option::None,
+ filter_code: std::option::Option::None,
+ origin: crate::AcquisitionObservationOrigin::Backfill,
+ detected_at: std::option::Option::None,
+ received_at: chrono::Utc::now(),
+ normalized_at: std::option::Option::None,
+ payload_size_bytes: std::option::Option::None,
+ source_payload_hash: std::option::Option::None,
+ status: crate::AcquisitionObservationStatus::Received,
+ error_code: std::option::Option::None,
+ error_message: std::option::Option::None,
+ };
+ assert!(observation.validate().is_err());
+ }
+
#[test]
fn transaction_observation_rejects_empty_provider() {
let result = crate::TransactionObservationInsert::new(
@@ -576,7 +741,7 @@ mod tests {
" ",
"solana_http",
"getTransaction",
- crate::TransactionObservationOrigin::Backfill,
+ crate::AcquisitionObservationOrigin::Backfill,
chrono::Utc::now(),
);
assert!(result.is_err());
@@ -589,7 +754,7 @@ mod tests {
"helius",
"solana_http",
"getTransaction",
- crate::TransactionObservationOrigin::Repair,
+ crate::AcquisitionObservationOrigin::Repair,
chrono::Utc::now(),
);
assert!(result.is_ok());
@@ -598,7 +763,7 @@ mod tests {
#[test]
fn raw_lifecycle_rejects_empty_reason_when_present() {
let result = crate::RawPayloadLifecycleMark::new(
- "kb_sol_raw_transactions",
+ "k_sol_raw_transactions",
"signature",
crate::RawPayloadRetentionState::Full,
crate::RawPayloadProcessingState::Received,
diff --git a/ks-store/src/contracts/dto/store.rs b/ks-store/src/contracts/dto/store.rs
index 29a47b1..ac1c1ca 100644
--- a/ks-store/src/contracts/dto/store.rs
+++ b/ks-store/src/contracts/dto/store.rs
@@ -1,22 +1,22 @@
// file: ks-store/src/contracts/dto/store.rs
-// version: 5
+// version: 6
//! Backend-agnostic store configuration and diagnostic DTOs.
-/// Store migration status contract.
+/// Store schema evolution status contract.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum StoreMigrationStatus {
- /// Migration status is not known yet.
+ /// Schema evolution status is not known yet.
Unknown,
- /// Store has no migration table or migration history yet.
+ /// Store baseline schema is not initialized yet.
NotInitialized,
- /// Store migrations are current.
+ /// Store schema contract is current.
Current,
- /// Store has pending migrations.
+ /// Store has pending schema evolution work.
Pending,
- /// Store migration history is inconsistent.
+ /// Store schema contract is partially applied or inconsistent.
Drift,
- /// Store migration check failed.
+ /// Store schema contract verification failed.
Failed,
}
@@ -99,21 +99,21 @@ impl crate::StoreBackendDescriptor {
}
}
-/// Store migration diagnostic snapshot contract.
+/// Store schema evolution diagnostic snapshot contract.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct StoreMigrationSnapshot {
- /// Current migration status.
+ /// Current schema evolution status.
pub status: crate::StoreMigrationStatus,
- /// Last applied migration identifier when known.
+ /// Current schema contract version when known.
pub current_version: std::option::Option,
- /// Pending migration identifiers when known.
+ /// Pending schema evolution identifiers when known.
pub pending_versions: std::vec::Vec,
/// Optional human-readable diagnostic message.
pub message: std::option::Option,
}
impl crate::StoreMigrationSnapshot {
- /// Builds a migration snapshot from explicit values.
+ /// Builds a schema evolution snapshot from explicit values.
pub fn new(
status: crate::StoreMigrationStatus,
current_version: std::option::Option,
diff --git a/ks-store/src/contracts/entity.rs b/ks-store/src/contracts/entity.rs
index 45e17e4..f2c361e 100644
--- a/ks-store/src/contracts/entity.rs
+++ b/ks-store/src/contracts/entity.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/entity.rs
-// version: 5
+// version: 6
//! Backend-neutral persisted entity exports for storage adapters.
@@ -8,6 +8,8 @@ mod raw;
/// Core account key persisted row contract.
pub use self::core::CoreAccountKeyRow;
+/// Core account-state persisted row contract.
+pub use self::core::CoreAccountStateRow;
/// Core balance change persisted row contract.
pub use self::core::CoreBalanceChangeRow;
/// Core inner instruction persisted row contract.
@@ -16,8 +18,12 @@ pub use self::core::CoreInnerInstructionRow;
pub use self::core::CoreInstructionRow;
/// Core log persisted row contract.
pub use self::core::CoreLogRow;
+/// Core return-data persisted row contract.
+pub use self::core::CoreReturnDataRow;
/// Core transaction persisted row contract.
pub use self::core::CoreTransactionRow;
+/// Generic account acquisition observation persisted row contract.
+pub use self::raw::AccountObservationRow;
/// Canonical raw Solana transaction persisted row contract.
pub use self::raw::RawTransactionRow;
/// Transaction acquisition observation persisted row contract.
diff --git a/ks-store/src/contracts/entity/core.rs b/ks-store/src/contracts/entity/core.rs
index 016f2f2..ab0a188 100644
--- a/ks-store/src/contracts/entity/core.rs
+++ b/ks-store/src/contracts/entity/core.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/entity/core.rs
-// version: 3
+// version: 4
//! Core Solana persisted entities.
@@ -12,6 +12,8 @@ pub struct CoreTransactionRow {
pub signature: std::string::String,
/// Transaction slot stored as SQL `BIGINT`.
pub slot: i64,
+ /// Optional block timestamp as Unix seconds supplied by Solana.
+ pub block_time: std::option::Option,
/// Whether the transaction failed on-chain.
pub failed: bool,
/// Optional canonical raw transaction row id used for lineage when available.
@@ -51,6 +53,35 @@ pub struct CoreAccountKeyRow {
pub created_at: chrono::DateTime,
}
+/// Canonical Core account-state persisted row contract.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CoreAccountStateRow {
+ /// Technical primary key.
+ pub id: i64,
+ /// Source account observation technical key.
+ pub source_observation_id: i64,
+ /// Account public key.
+ pub account_key: std::string::String,
+ /// Context slot stored as SQL `BIGINT`.
+ pub slot: i64,
+ /// Owning program id.
+ pub owner: std::string::String,
+ /// Exact lamports representation returned by PostgreSQL.
+ pub lamports: std::string::String,
+ /// Whether the account is executable.
+ pub executable: bool,
+ /// Exact rent-epoch representation returned by PostgreSQL.
+ pub rent_epoch: std::string::String,
+ /// Complete account-data length.
+ pub space: i64,
+ /// Exact account bytes encoded as standard base64.
+ pub data_base64: std::string::String,
+ /// Deterministic digest of decoded account bytes.
+ pub data_hash: std::string::String,
+ /// Insert timestamp.
+ pub created_at: chrono::DateTime,
+}
+
/// Core instruction persisted row contract.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct CoreInstructionRow {
@@ -66,6 +97,8 @@ pub struct CoreInstructionRow {
pub instruction_path: std::string::String,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
+ /// Optional runtime invocation stack height.
+ pub stack_height: std::option::Option,
/// Instruction accounts as JSON, preserving unresolved forms when needed.
pub accounts_json: serde_json::Value,
/// Instruction payload JSON while it is still retained in the hot store.
@@ -97,12 +130,37 @@ pub struct CoreInnerInstructionRow {
pub instruction_path: std::string::String,
/// Program id as non-empty base58 text.
pub program_id: std::string::String,
+ /// Optional runtime invocation stack height.
+ pub stack_height: std::option::Option,
/// Inner instruction accounts as JSON.
pub accounts_json: serde_json::Value,
/// Inner instruction payload JSON while it is retained in the hot store.
pub payload_json: std::option::Option,
/// Optional digest of the inner instruction payload after compaction or purge.
pub payload_json_hash: std::option::Option,
+ /// Current processing state used by CPI replay.
+ pub processing_state: crate::CoreInstructionProcessingState,
+ /// Insert timestamp.
+ pub created_at: chrono::DateTime,
+ /// Last lifecycle update timestamp.
+ pub updated_at: chrono::DateTime,
+}
+
+/// Core return-data persisted row contract.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CoreReturnDataRow {
+ /// Technical primary key.
+ pub id: i64,
+ /// Parent transaction technical key.
+ pub transaction_id: i64,
+ /// Transaction signature.
+ pub signature: std::string::String,
+ /// Transaction slot stored as SQL `BIGINT`.
+ pub slot: i64,
+ /// Program id that produced the return data.
+ pub program_id: std::string::String,
+ /// Exact return bytes encoded as standard base64.
+ pub data_base64: std::string::String,
/// Insert timestamp.
pub created_at: chrono::DateTime,
}
diff --git a/ks-store/src/contracts/entity/raw.rs b/ks-store/src/contracts/entity/raw.rs
index 4f489d5..c48ef01 100644
--- a/ks-store/src/contracts/entity/raw.rs
+++ b/ks-store/src/contracts/entity/raw.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/entity/raw.rs
-// version: 3
+// version: 4
//! Canonical Solana transaction and acquisition observation persisted entities.
@@ -12,6 +12,8 @@ pub struct RawTransactionRow {
pub signature: std::string::String,
/// Transaction slot stored as SQL `BIGINT`.
pub slot: i64,
+ /// Optional block timestamp as Unix seconds supplied by Solana.
+ pub block_time: std::option::Option,
/// Canonical source-independent transaction document while retained in the hot store.
pub canonical_json: std::option::Option,
/// Optional deterministic digest of the canonical document.
@@ -50,7 +52,7 @@ pub struct TransactionObservationRow {
/// Acquisition method, for example `getTransaction`, `transactionSubscribe` or `transactions`.
pub acquisition_method: std::string::String,
/// Acquisition origin category.
- pub origin: crate::TransactionObservationOrigin,
+ pub origin: crate::AcquisitionObservationOrigin,
/// Optional Solana commitment.
pub commitment: std::option::Option,
/// Optional capture session identifier.
@@ -70,9 +72,70 @@ pub struct TransactionObservationRow {
/// Optional digest of the source-specific payload without retaining that payload.
pub source_payload_hash: std::option::Option,
/// Current observation status.
- pub status: crate::TransactionObservationStatus,
+ pub status: crate::AcquisitionObservationStatus,
/// Optional machine-readable error code.
pub error_code: std::option::Option,
/// Optional diagnostic error message.
pub error_message: std::option::Option,
}
+
+/// Generic account acquisition observation persisted row contract.
+#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct AccountObservationRow {
+ /// Technical primary key.
+ pub id: i64,
+ /// Stable observation key.
+ pub observation_key: std::string::String,
+ /// Observed account public key.
+ pub account_key: std::string::String,
+ /// Context slot stored as SQL `BIGINT`.
+ pub context_slot: i64,
+ /// Account owner when present.
+ pub owner: std::option::Option,
+ /// Exact lamports rendered as decimal text when present.
+ pub lamports: std::option::Option,
+ /// Executable flag when present.
+ pub executable: std::option::Option,
+ /// Exact rent epoch rendered as decimal text when present.
+ pub rent_epoch: std::option::Option,
+ /// Account data length when present.
+ pub space: std::option::Option,
+ /// Exact account bytes encoded as standard base64 when captured.
+ pub data_base64: std::option::Option,
+ /// Deterministic digest of decoded account bytes when captured.
+ pub data_hash: std::option::Option,
+ /// Acquisition provider code.
+ pub provider: std::string::String,
+ /// Optional endpoint code.
+ pub endpoint_code: std::option::Option,
+ /// Acquisition protocol.
+ pub protocol: std::string::String,
+ /// Acquisition method.
+ pub acquisition_method: std::string::String,
+ /// Optional commitment.
+ pub commitment: std::option::Option,
+ /// Optional capture session id.
+ pub capture_session_id: std::option::Option,
+ /// Optional configured filter code.
+ pub filter_code: std::option::Option,
+ /// Acquisition origin.
+ pub origin: crate::AcquisitionObservationOrigin,
+ /// Optional detection timestamp.
+ pub detected_at: std::option::Option>,
+ /// Receive timestamp.
+ pub received_at: chrono::DateTime,
+ /// Optional normalization timestamp.
+ pub normalized_at: std::option::Option>,
+ /// Persist timestamp.
+ pub persisted_at: chrono::DateTime,
+ /// Optional uncompressed source payload size.
+ pub payload_size_bytes: std::option::Option,
+ /// Optional source-specific payload digest.
+ pub source_payload_hash: std::option::Option,
+ /// Technical observation status.
+ pub status: crate::AcquisitionObservationStatus,
+ /// Optional error code.
+ pub error_code: std::option::Option,
+ /// Optional diagnostic message.
+ pub error_message: std::option::Option,
+}
diff --git a/ks-store/src/contracts/health.rs b/ks-store/src/contracts/health.rs
index 4fd68b8..27e64ae 100644
--- a/ks-store/src/contracts/health.rs
+++ b/ks-store/src/contracts/health.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/health.rs
-// version: 2
+// version: 3
//! Backend-neutral health contracts for storage implementations.
@@ -22,16 +22,16 @@ pub struct StoreHealthSnapshot {
/// Stable backend code such as `postgres` or `sqlite`.
pub backend: std::string::String,
/// Current health status.
- pub status: StoreHealthStatus,
+ pub status: crate::StoreHealthStatus,
/// Optional human-readable diagnostic message.
pub message: std::option::Option,
}
-impl StoreHealthSnapshot {
+impl crate::StoreHealthSnapshot {
/// Builds a store health snapshot after minimal validation.
pub fn new(
backend: impl std::convert::Into,
- status: StoreHealthStatus,
+ status: crate::StoreHealthStatus,
message: std::option::Option,
) -> ks_core::Result {
let backend_value = backend.into();
diff --git a/ks-store/src/contracts/pagination.rs b/ks-store/src/contracts/pagination.rs
index 015d9ec..0805c09 100644
--- a/ks-store/src/contracts/pagination.rs
+++ b/ks-store/src/contracts/pagination.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/pagination.rs
-// version: 2
+// version: 3
//! Backend-neutral pagination and sorting contracts for repository operations.
@@ -26,7 +26,7 @@ pub struct PageRequest {
pub offset: u64,
}
-impl PageRequest {
+impl crate::PageRequest {
/// Builds a page request after minimal bounds validation.
pub fn new(limit: u16, offset: u64) -> ks_core::Result {
if limit == 0 {
diff --git a/ks-store/src/contracts/repository.rs b/ks-store/src/contracts/repository.rs
index fd92587..cf8b43c 100644
--- a/ks-store/src/contracts/repository.rs
+++ b/ks-store/src/contracts/repository.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/contracts/repository.rs
-// version: 3
+// version: 4
//! Storage trait definitions shared by concrete stores.
@@ -187,8 +187,8 @@ pub trait DecodePipelineStore {
) -> ks_core::Result>;
/// Lists bounded materialized outputs for read-only application views.
- async fn list_materialized_events(
+ async fn list_materialized_outputs(
&self,
- filter: &crate::MaterializedEventFilter,
- ) -> ks_core::Result>;
+ filter: &crate::MaterializedOutputFilter,
+ ) -> ks_core::Result>;
}
diff --git a/ks-store/src/lib.rs b/ks-store/src/lib.rs
index f219500..30c331a 100644
--- a/ks-store/src/lib.rs
+++ b/ks-store/src/lib.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/lib.rs
-// version: 10
+// version: 12
//! Backend-agnostic Solana storage contracts and store facade.
#![warn(missing_docs)]
@@ -13,8 +13,12 @@ mod store;
/// Canonical crate-internal tracing target shared through the crate-root facade.
pub(crate) use self::constants::TRACING_TARGET;
+/// Crate-internal storage symbol `ACCOUNT_OBSERVATIONS_TABLE_NAME` shared through the crate-root facade.
+pub(crate) use self::postgres::ACCOUNT_OBSERVATIONS_TABLE_NAME;
/// Crate-internal storage symbol `CORE_ACCOUNT_KEYS_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::CORE_ACCOUNT_KEYS_TABLE_NAME;
+/// Crate-internal storage symbol `CORE_ACCOUNT_STATES_TABLE_NAME` shared through the crate-root facade.
+pub(crate) use self::postgres::CORE_ACCOUNT_STATES_TABLE_NAME;
/// Crate-internal storage symbol `CORE_BALANCE_CHANGES_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::CORE_BALANCE_CHANGES_TABLE_NAME;
/// Crate-internal storage symbol `CORE_INNER_INSTRUCTIONS_TABLE_NAME` shared through the crate-root facade.
@@ -23,6 +27,8 @@ pub(crate) use self::postgres::CORE_INNER_INSTRUCTIONS_TABLE_NAME;
pub(crate) use self::postgres::CORE_INSTRUCTIONS_TABLE_NAME;
/// Crate-internal storage symbol `CORE_LOGS_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::CORE_LOGS_TABLE_NAME;
+/// Crate-internal storage symbol `CORE_RETURN_DATA_TABLE_NAME` shared through the crate-root facade.
+pub(crate) use self::postgres::CORE_RETURN_DATA_TABLE_NAME;
/// Crate-internal storage symbol `CORE_TRANSACTIONS_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::CORE_TRANSACTIONS_TABLE_NAME;
/// Crate-internal storage symbol `DECODE_COVERAGE_DECLARATIONS_TABLE_NAME` shared through the crate-root facade.
@@ -31,8 +37,10 @@ pub(crate) use self::postgres::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME;
pub(crate) use self::postgres::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME;
/// Crate-internal storage symbol `DECODE_EVENTS_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::DECODE_EVENTS_TABLE_NAME;
-/// Crate-internal storage symbol `MATERIALIZED_EVENTS_TABLE_NAME` shared through the crate-root facade.
-pub(crate) use self::postgres::MATERIALIZED_EVENTS_TABLE_NAME;
+/// Crate-internal storage symbol `LEGACY_SOLANA_TABLE_PREFIX` shared through the crate-root facade.
+pub(crate) use self::postgres::LEGACY_SOLANA_TABLE_PREFIX;
+/// Crate-internal storage symbol `MATERIALIZED_OUTPUTS_TABLE_NAME` shared through the crate-root facade.
+pub(crate) use self::postgres::MATERIALIZED_OUTPUTS_TABLE_NAME;
/// Crate-internal storage symbol `PROCESSING_LEDGER_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::PROCESSING_LEDGER_TABLE_NAME;
/// Crate-internal storage symbol `PostgresStore` shared through the crate-root facade.
@@ -47,12 +55,8 @@ pub(crate) use self::postgres::RAW_TRANSACTIONS_TABLE_NAME;
pub(crate) use self::postgres::STORE_SCHEMA_ADVISORY_LOCK_ID;
/// Crate-internal storage symbol `TRANSACTION_OBSERVATIONS_TABLE_NAME` shared through the crate-root facade.
pub(crate) use self::postgres::TRANSACTION_OBSERVATIONS_TABLE_NAME;
-/// Crate-internal storage symbol `apply_core_store_schema` shared through the crate-root facade.
-pub(crate) use self::postgres::apply_core_store_schema;
-/// Crate-internal storage symbol `apply_decode_store_schema` shared through the crate-root facade.
-pub(crate) use self::postgres::apply_decode_store_schema;
-/// Crate-internal storage symbol `apply_raw_store_schema` shared through the crate-root facade.
-pub(crate) use self::postgres::apply_raw_store_schema;
+/// Crate-internal complete PostgreSQL baseline initializer shared through the crate-root facade.
+pub(crate) use self::postgres::apply_store_schema;
/// Crate-internal storage symbol `core_store_schema_statements` shared through the crate-root facade.
pub(crate) use self::postgres::core_store_schema_statements;
/// Crate-internal storage symbol `core_store_table_diagnostic_specs` shared through the crate-root facade.
@@ -61,6 +65,8 @@ pub(crate) use self::postgres::core_store_table_diagnostic_specs;
pub(crate) use self::postgres::decode_store_schema_statements;
/// Crate-internal storage symbol `decode_store_table_diagnostic_specs` shared through the crate-root facade.
pub(crate) use self::postgres::decode_store_table_diagnostic_specs;
+/// Crate-internal expected PostgreSQL index inventory shared through the crate-root facade.
+pub(crate) use self::postgres::expected_postgres_index_names;
/// Crate-internal storage symbol `has_raw_transaction_signature` shared through the crate-root facade.
pub(crate) use self::postgres::has_raw_transaction_signature;
/// Crate-internal storage symbol `has_transaction_observation_key` shared through the crate-root facade.
@@ -95,8 +101,8 @@ pub(crate) use self::postgres::list_decode_coverage_summary;
pub(crate) use self::postgres::list_decode_inputs;
/// Crate-internal storage symbol `list_decode_replay_inputs` shared through the crate-root facade.
pub(crate) use self::postgres::list_decode_replay_inputs;
-/// Crate-internal storage symbol `list_materialized_events` shared through the crate-root facade.
-pub(crate) use self::postgres::list_materialized_events;
+/// Crate-internal storage symbol `list_materialized_outputs` shared through the crate-root facade.
+pub(crate) use self::postgres::list_materialized_outputs;
/// Crate-internal storage symbol `list_raw_transactions_for_core_extraction` shared through the crate-root facade.
pub(crate) use self::postgres::list_raw_transactions_for_core_extraction;
/// Crate-internal storage symbol `list_replay_entity_summaries` shared through the crate-root facade.
@@ -107,10 +113,8 @@ pub(crate) use self::postgres::list_replay_program_summaries;
pub(crate) use self::postgres::list_replay_transaction_candidates;
/// Crate-internal storage symbol `load_current_schema` shared through the crate-root facade.
pub(crate) use self::postgres::load_current_schema;
-/// Crate-internal storage symbol `load_latest_migration_version` shared through the crate-root facade.
-pub(crate) use self::postgres::load_latest_migration_version;
-/// Crate-internal storage symbol `load_migration_table_name` shared through the crate-root facade.
-pub(crate) use self::postgres::load_migration_table_name;
+/// Crate-internal expected PostgreSQL index counts shared through the crate-root facade.
+pub(crate) use self::postgres::load_expected_index_counts;
/// Crate-internal storage symbol `load_server_version` shared through the crate-root facade.
pub(crate) use self::postgres::load_server_version;
/// Crate-internal storage symbol `load_table_statistics` shared through the crate-root facade.
@@ -138,51 +142,65 @@ pub(crate) use self::postgres::raw_store_table_diagnostic_specs;
pub(crate) use self::postgres::run_health_check;
/// Crate-internal storage symbol `table_exists` shared through the crate-root facade.
pub(crate) use self::postgres::table_exists;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_account_keys_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_account_keys_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_balance_changes_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_balance_changes_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_inner_instructions_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_inner_instructions_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_instructions_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_instructions_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_logs_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_logs_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_core_transactions_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_core_transactions_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_decode_coverage_declarations_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_decode_coverage_declarations_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_decode_coverage_observations_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_decode_coverage_observations_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_decode_events_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_decode_events_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_mat_events_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_mat_events_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_obs_transaction_observations_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_obs_transaction_observations_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_ops_processing_ledger_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_ops_processing_ledger_sql;
-/// Crate-internal storage symbol `table_stats_kb_sol_raw_transactions_sql` shared through the crate-root facade.
-pub(crate) use self::postgres::table_stats_kb_sol_raw_transactions_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_account_keys_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_account_keys_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_account_states_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_account_states_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_balance_changes_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_balance_changes_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_inner_instructions_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_inner_instructions_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_instructions_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_instructions_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_logs_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_logs_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_return_data_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_return_data_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_core_transactions_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_core_transactions_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_decode_coverage_declarations_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_decode_coverage_declarations_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_decode_coverage_observations_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_decode_coverage_observations_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_decode_events_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_decode_events_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_mat_outputs_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_mat_outputs_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_obs_account_observations_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_obs_account_observations_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_obs_transaction_observations_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_obs_transaction_observations_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_ops_processing_ledger_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_ops_processing_ledger_sql;
+/// Crate-internal storage symbol `table_stats_k_sol_raw_transactions_sql` shared through the crate-root facade.
+pub(crate) use self::postgres::table_stats_k_sol_raw_transactions_sql;
/// Crate-internal storage symbol `update_core_instruction_lifecycle` shared through the crate-root facade.
pub(crate) use self::postgres::update_core_instruction_lifecycle;
/// Crate-internal storage symbol `update_raw_payload_lifecycle` shared through the crate-root facade.
pub(crate) use self::postgres::update_raw_payload_lifecycle;
-/// Crate-internal storage symbol `validate_core_store_table_names` shared through the crate-root facade.
-pub(crate) use self::postgres::validate_core_store_table_names;
-/// Crate-internal storage symbol `validate_decode_store_table_names` shared through the crate-root facade.
-pub(crate) use self::postgres::validate_decode_store_table_names;
-/// Crate-internal storage symbol `validate_raw_store_table_names` shared through the crate-root facade.
-pub(crate) use self::postgres::validate_raw_store_table_names;
+/// Crate-internal PostgreSQL SQL-resource canary shared through the crate-root facade.
+pub(crate) use self::postgres::validate_postgres_sql_resources;
/// Transitional logical schema contract identifier used until the `0.5.3-pre.3` rebuild.
pub use self::constants::STORE_SCHEMA_CONTRACT_VERSION;
+/// Generic account acquisition observation insert contract.
+pub use self::contracts::AccountObservationInsert;
+/// Generic account acquisition observation persisted row contract.
+pub use self::contracts::AccountObservationRow;
+/// Transaction acquisition observation origin.
+pub use self::contracts::AcquisitionObservationOrigin;
+/// Generic account acquisition observation status.
+pub use self::contracts::AcquisitionObservationStatus;
/// Core account key insert contract.
pub use self::contracts::CoreAccountKeyInsert;
/// Core account key persisted row contract.
pub use self::contracts::CoreAccountKeyRow;
/// Core account key source category.
pub use self::contracts::CoreAccountKeySource;
+/// Core account-state insert contract.
+pub use self::contracts::CoreAccountStateInsert;
+/// Core account-state persisted row contract.
+pub use self::contracts::CoreAccountStateRow;
/// Core balance change insert contract.
pub use self::contracts::CoreBalanceChangeInsert;
/// Core balance change kind.
@@ -215,6 +233,10 @@ pub use self::contracts::CoreInstructionRow;
pub use self::contracts::CoreLogInsert;
/// Core log persisted row contract.
pub use self::contracts::CoreLogRow;
+/// Core return-data insert contract.
+pub use self::contracts::CoreReturnDataInsert;
+/// Core return-data persisted row contract.
+pub use self::contracts::CoreReturnDataRow;
/// Core transaction insert contract.
pub use self::contracts::CoreTransactionInsert;
/// Core transaction persisted row contract.
@@ -237,24 +259,26 @@ pub use self::contracts::DecodeObservationInsert;
pub use self::contracts::DecodePersistenceBundle;
/// Contextual instruction decode and materialization storage behavior.
pub use self::contracts::DecodePipelineStore;
+/// Source-neutral external decode schema provenance.
+pub use self::contracts::DecodeSchemaProvenance;
/// Bounded contextual instruction selection filter for decode campaigns.
pub use self::contracts::DecodeSelectionFilter;
/// Insert or upsert result contract returned by repositories.
pub use self::contracts::InsertOutcome;
/// Maximum number of materialized rows returned by one bounded query.
-pub use self::contracts::MAX_MATERIALIZED_EVENT_QUERY_ROWS;
+pub use self::contracts::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS;
/// Maximum repository page size.
pub use self::contracts::MAX_PAGE_SIZE;
/// Maximum number of rows returned by one replay candidate query.
pub use self::contracts::MAX_REPLAY_CANDIDATE_ROWS;
/// Atomic persistence bundle for one materializer and one decoded observation.
pub use self::contracts::MaterializationPersistenceBundle;
-/// Bounded read-only materialized event selection.
-pub use self::contracts::MaterializedEventFilter;
-/// One materialized output returned by a bounded query.
-pub use self::contracts::MaterializedEventQueryRow;
+/// Bounded read-only materialized output selection.
+pub use self::contracts::MaterializedOutputFilter;
/// One processor-owned generic materialized output row.
pub use self::contracts::MaterializedOutputInsert;
+/// One materialized output returned by a bounded query.
+pub use self::contracts::MaterializedOutputQueryRow;
/// Page request contract for repository list operations.
pub use self::contracts::PageRequest;
/// Stable processing ledger identity.
@@ -325,12 +349,8 @@ pub use self::contracts::StoreResourceStatistics;
pub use self::contracts::StoreRuntimeSummary;
/// Transaction acquisition observation insert contract.
pub use self::contracts::TransactionObservationInsert;
-/// Transaction acquisition observation origin.
-pub use self::contracts::TransactionObservationOrigin;
/// Transaction acquisition observation persisted row contract.
pub use self::contracts::TransactionObservationRow;
-/// Transaction acquisition observation status.
-pub use self::contracts::TransactionObservationStatus;
/// Storage error helper functions.
pub use self::contracts::storage_contract_error;
/// Backend-agnostic persistent store facade.
diff --git a/ks-store/src/postgres.rs b/ks-store/src/postgres.rs
index 117eca7..c0f831a 100644
--- a/ks-store/src/postgres.rs
+++ b/ks-store/src/postgres.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/postgres.rs
-// version: 11
+// version: 13
//! Private PostgreSQL implementation of the backend-agnostic store contracts.
@@ -10,16 +10,20 @@ mod store;
#[cfg(test)]
mod test_serial;
+pub(crate) use self::migrations::ACCOUNT_OBSERVATIONS_TABLE_NAME;
pub(crate) use self::migrations::CORE_ACCOUNT_KEYS_TABLE_NAME;
+pub(crate) use self::migrations::CORE_ACCOUNT_STATES_TABLE_NAME;
pub(crate) use self::migrations::CORE_BALANCE_CHANGES_TABLE_NAME;
pub(crate) use self::migrations::CORE_INNER_INSTRUCTIONS_TABLE_NAME;
pub(crate) use self::migrations::CORE_INSTRUCTIONS_TABLE_NAME;
pub(crate) use self::migrations::CORE_LOGS_TABLE_NAME;
+pub(crate) use self::migrations::CORE_RETURN_DATA_TABLE_NAME;
pub(crate) use self::migrations::CORE_TRANSACTIONS_TABLE_NAME;
pub(crate) use self::migrations::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME;
pub(crate) use self::migrations::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME;
pub(crate) use self::migrations::DECODE_EVENTS_TABLE_NAME;
-pub(crate) use self::migrations::MATERIALIZED_EVENTS_TABLE_NAME;
+pub(crate) use self::migrations::LEGACY_SOLANA_TABLE_PREFIX;
+pub(crate) use self::migrations::MATERIALIZED_OUTPUTS_TABLE_NAME;
pub(crate) use self::migrations::PROCESSING_LEDGER_TABLE_NAME;
pub(crate) use self::migrations::PostgresTableDiagnosticSpec;
pub(crate) use self::migrations::RAW_TRANSACTIONS_TABLE_NAME;
@@ -29,27 +33,27 @@ pub(crate) use self::migrations::core_store_schema_statements;
pub(crate) use self::migrations::core_store_table_diagnostic_specs;
pub(crate) use self::migrations::decode_store_schema_statements;
pub(crate) use self::migrations::decode_store_table_diagnostic_specs;
+pub(crate) use self::migrations::expected_postgres_index_names;
pub(crate) use self::migrations::raw_store_schema_statements;
pub(crate) use self::migrations::raw_store_table_diagnostic_specs;
-pub(crate) use self::migrations::table_stats_kb_sol_core_account_keys_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_core_balance_changes_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_core_inner_instructions_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_core_instructions_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_core_logs_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_core_transactions_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_decode_coverage_declarations_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_decode_coverage_observations_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_decode_events_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_mat_events_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_obs_transaction_observations_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_ops_processing_ledger_sql;
-pub(crate) use self::migrations::table_stats_kb_sol_raw_transactions_sql;
-pub(crate) use self::migrations::validate_core_store_table_names;
-pub(crate) use self::migrations::validate_decode_store_table_names;
-pub(crate) use self::migrations::validate_raw_store_table_names;
-pub(crate) use self::query::apply_core_store_schema;
-pub(crate) use self::query::apply_decode_store_schema;
-pub(crate) use self::query::apply_raw_store_schema;
+pub(crate) use self::migrations::table_stats_k_sol_core_account_keys_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_account_states_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_balance_changes_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_inner_instructions_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_instructions_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_logs_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_return_data_sql;
+pub(crate) use self::migrations::table_stats_k_sol_core_transactions_sql;
+pub(crate) use self::migrations::table_stats_k_sol_decode_coverage_declarations_sql;
+pub(crate) use self::migrations::table_stats_k_sol_decode_coverage_observations_sql;
+pub(crate) use self::migrations::table_stats_k_sol_decode_events_sql;
+pub(crate) use self::migrations::table_stats_k_sol_mat_outputs_sql;
+pub(crate) use self::migrations::table_stats_k_sol_obs_account_observations_sql;
+pub(crate) use self::migrations::table_stats_k_sol_obs_transaction_observations_sql;
+pub(crate) use self::migrations::table_stats_k_sol_ops_processing_ledger_sql;
+pub(crate) use self::migrations::table_stats_k_sol_raw_transactions_sql;
+pub(crate) use self::migrations::validate_postgres_sql_resources;
+pub(crate) use self::query::apply_store_schema;
pub(crate) use self::query::has_raw_transaction_signature;
pub(crate) use self::query::has_transaction_observation_key;
pub(crate) use self::query::insert_core_account_keys;
@@ -67,14 +71,13 @@ pub(crate) use self::query::list_core_instructions_for_replay;
pub(crate) use self::query::list_decode_coverage_summary;
pub(crate) use self::query::list_decode_inputs;
pub(crate) use self::query::list_decode_replay_inputs;
-pub(crate) use self::query::list_materialized_events;
+pub(crate) use self::query::list_materialized_outputs;
pub(crate) use self::query::list_raw_transactions_for_core_extraction;
pub(crate) use self::query::list_replay_entity_summaries;
pub(crate) use self::query::list_replay_program_summaries;
pub(crate) use self::query::list_replay_transaction_candidates;
pub(crate) use self::query::load_current_schema;
-pub(crate) use self::query::load_latest_migration_version;
-pub(crate) use self::query::load_migration_table_name;
+pub(crate) use self::query::load_expected_index_counts;
pub(crate) use self::query::load_server_version;
pub(crate) use self::query::load_table_statistics;
pub(crate) use self::query::mark_core_extraction_failed;
diff --git a/ks-store/src/postgres/migrations.rs b/ks-store/src/postgres/migrations.rs
index 31b0c28..10ba774 100644
--- a/ks-store/src/postgres/migrations.rs
+++ b/ks-store/src/postgres/migrations.rs
@@ -1,44 +1,57 @@
// file: ks-store/src/postgres/migrations.rs
-// version: 7
+// version: 10
-//! PostgreSQL migration conventions for the storage backend.
+//! PostgreSQL schema resources and migration conventions for the storage backend.
/// Canonical Solana table prefix.
-const SOLANA_TABLE_PREFIX: &str = "kb_sol_";
+#[cfg(test)]
+const SOLANA_TABLE_PREFIX: &str = "k_sol_";
+/// Historical table prefix rejected by the `0.5.3` baseline initializer.
+pub(crate) const LEGACY_SOLANA_TABLE_PREFIX: &str = "kb_sol_";
/// Advisory lock id used while applying idempotent store schema statements.
-pub(crate) const STORE_SCHEMA_ADVISORY_LOCK_ID: i64 = 2_024_000_204;
-/// Canonical raw transaction table name.
-pub(crate) const RAW_TRANSACTIONS_TABLE_NAME: &str = "kb_sol_raw_transactions";
-/// Lightweight transaction acquisition observation table name.
-pub(crate) const TRANSACTION_OBSERVATIONS_TABLE_NAME: &str = "kb_sol_obs_transaction_observations";
-/// Core transaction table name.
-pub(crate) const CORE_TRANSACTIONS_TABLE_NAME: &str = "kb_sol_core_transactions";
-/// Core account key table name.
-pub(crate) const CORE_ACCOUNT_KEYS_TABLE_NAME: &str = "kb_sol_core_account_keys";
-/// Core instruction table name.
-pub(crate) const CORE_INSTRUCTIONS_TABLE_NAME: &str = "kb_sol_core_instructions";
-/// Core inner instruction table name.
-pub(crate) const CORE_INNER_INSTRUCTIONS_TABLE_NAME: &str = "kb_sol_core_inner_instructions";
-/// Core log table name.
-pub(crate) const CORE_LOGS_TABLE_NAME: &str = "kb_sol_core_logs";
-/// Core balance change table name.
-pub(crate) const CORE_BALANCE_CHANGES_TABLE_NAME: &str = "kb_sol_core_balance_changes";
-/// Processing ledger table name.
-pub(crate) const PROCESSING_LEDGER_TABLE_NAME: &str = "kb_sol_ops_processing_ledger";
-/// Versioned decoded event table name.
-pub(crate) const DECODE_EVENTS_TABLE_NAME: &str = "kb_sol_decode_events";
-/// Machine-readable decoder coverage declaration table name.
+pub(crate) const STORE_SCHEMA_ADVISORY_LOCK_ID: i64 = 2_024_000_503;
+/// PostgreSQL table `k_sol_raw_transactions`.
+pub(crate) const RAW_TRANSACTIONS_TABLE_NAME: &str = "k_sol_raw_transactions";
+/// PostgreSQL table `k_sol_obs_transaction_observations`.
+pub(crate) const TRANSACTION_OBSERVATIONS_TABLE_NAME: &str = "k_sol_obs_transaction_observations";
+/// PostgreSQL table `k_sol_obs_account_observations`.
+pub(crate) const ACCOUNT_OBSERVATIONS_TABLE_NAME: &str = "k_sol_obs_account_observations";
+/// PostgreSQL table `k_sol_core_transactions`.
+pub(crate) const CORE_TRANSACTIONS_TABLE_NAME: &str = "k_sol_core_transactions";
+/// PostgreSQL table `k_sol_core_account_keys`.
+pub(crate) const CORE_ACCOUNT_KEYS_TABLE_NAME: &str = "k_sol_core_account_keys";
+/// PostgreSQL table `k_sol_core_instructions`.
+pub(crate) const CORE_INSTRUCTIONS_TABLE_NAME: &str = "k_sol_core_instructions";
+/// PostgreSQL table `k_sol_core_inner_instructions`.
+pub(crate) const CORE_INNER_INSTRUCTIONS_TABLE_NAME: &str = "k_sol_core_inner_instructions";
+/// PostgreSQL table `k_sol_core_logs`.
+pub(crate) const CORE_LOGS_TABLE_NAME: &str = "k_sol_core_logs";
+/// PostgreSQL table `k_sol_core_balance_changes`.
+pub(crate) const CORE_BALANCE_CHANGES_TABLE_NAME: &str = "k_sol_core_balance_changes";
+/// PostgreSQL table `k_sol_core_return_data`.
+pub(crate) const CORE_RETURN_DATA_TABLE_NAME: &str = "k_sol_core_return_data";
+/// PostgreSQL table `k_sol_core_account_states`.
+pub(crate) const CORE_ACCOUNT_STATES_TABLE_NAME: &str = "k_sol_core_account_states";
+/// PostgreSQL table `k_sol_ops_processing_ledger`.
+pub(crate) const PROCESSING_LEDGER_TABLE_NAME: &str = "k_sol_ops_processing_ledger";
+/// PostgreSQL table `k_sol_decode_events`.
+pub(crate) const DECODE_EVENTS_TABLE_NAME: &str = "k_sol_decode_events";
+/// PostgreSQL table `k_sol_decode_coverage_declarations`.
pub(crate) const DECODE_COVERAGE_DECLARATIONS_TABLE_NAME: &str =
- "kb_sol_decode_coverage_declarations";
-/// Observed decoder coverage classification table name.
+ "k_sol_decode_coverage_declarations";
+/// PostgreSQL table `k_sol_decode_coverage_observations`.
pub(crate) const DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME: &str =
- "kb_sol_decode_coverage_observations";
-/// Versioned materialized output table name.
-pub(crate) const MATERIALIZED_EVENTS_TABLE_NAME: &str = "kb_sol_mat_events";
-/// Canonical transaction acquisition table names active since `0.3.1`.
-const RAW_STORE_TABLE_NAMES: &[&str] =
- &[RAW_TRANSACTIONS_TABLE_NAME, TRANSACTION_OBSERVATIONS_TABLE_NAME];
-/// Core Solana table names introduced by `0.2.4`.
+ "k_sol_decode_coverage_observations";
+/// PostgreSQL table `k_sol_mat_outputs`.
+pub(crate) const MATERIALIZED_OUTPUTS_TABLE_NAME: &str = "k_sol_mat_outputs";
+
+#[cfg(test)]
+const RAW_STORE_TABLE_NAMES: &[&str] = &[
+ RAW_TRANSACTIONS_TABLE_NAME,
+ TRANSACTION_OBSERVATIONS_TABLE_NAME,
+ ACCOUNT_OBSERVATIONS_TABLE_NAME,
+];
+#[cfg(test)]
const CORE_STORE_TABLE_NAMES: &[&str] = &[
CORE_TRANSACTIONS_TABLE_NAME,
CORE_ACCOUNT_KEYS_TABLE_NAME,
@@ -46,18 +59,102 @@ const CORE_STORE_TABLE_NAMES: &[&str] = &[
CORE_INNER_INSTRUCTIONS_TABLE_NAME,
CORE_LOGS_TABLE_NAME,
CORE_BALANCE_CHANGES_TABLE_NAME,
+ CORE_RETURN_DATA_TABLE_NAME,
+ CORE_ACCOUNT_STATES_TABLE_NAME,
PROCESSING_LEDGER_TABLE_NAME,
];
-/// Decode and materialization table names introduced by `0.4.0`.
+#[cfg(test)]
const DECODE_STORE_TABLE_NAMES: &[&str] = &[
DECODE_EVENTS_TABLE_NAME,
DECODE_COVERAGE_DECLARATIONS_TABLE_NAME,
DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME,
- MATERIALIZED_EVENTS_TABLE_NAME,
+ MATERIALIZED_OUTPUTS_TABLE_NAME,
];
-/// Allowed Solana table domains encoded after the `kb_sol_` prefix.
+#[cfg(test)]
const ALLOWED_SOLANA_TABLE_DOMAINS: &[&str] =
&["raw", "core", "obs", "decode", "mat", "catalog", "agg", "ops", "wallet"];
+const EXPECTED_POSTGRES_INDEX_NAMES: &[&str] = &[
+ "ix_k_sol_core_account_keys_key",
+ "ix_k_sol_core_account_states_account_slot",
+ "ix_k_sol_core_account_states_owner",
+ "ix_k_sol_core_balance_changes_account",
+ "ix_k_sol_core_balance_changes_mint",
+ "ix_k_sol_core_balance_changes_owner",
+ "ix_k_sol_core_inner_instructions_parent",
+ "ix_k_sol_core_inner_instructions_processing",
+ "ix_k_sol_core_inner_instructions_program",
+ "ix_k_sol_core_instructions_processing",
+ "ix_k_sol_core_instructions_program",
+ "ix_k_sol_core_instructions_slot",
+ "ix_k_sol_core_logs_path",
+ "ix_k_sol_core_logs_program",
+ "ix_k_sol_core_return_data_program",
+ "ix_k_sol_core_transactions_created_at",
+ "ix_k_sol_core_transactions_slot",
+ "ix_k_sol_decode_coverage_declarations_program",
+ "ix_k_sol_decode_coverage_observations_entry",
+ "ix_k_sol_decode_coverage_observations_program_status",
+ "ix_k_sol_decode_events_family_commit",
+ "ix_k_sol_decode_events_program_surface",
+ "ix_k_sol_decode_events_signature_path",
+ "ix_k_sol_mat_outputs_processor_family_slot",
+ "ix_k_sol_mat_outputs_signature_family",
+ "ix_k_sol_obs_account_observations_account_slot",
+ "ix_k_sol_obs_account_observations_provider_method",
+ "ix_k_sol_obs_account_observations_received_at",
+ "ix_k_sol_obs_account_observations_status",
+ "ix_k_sol_obs_transaction_observations_provider_method",
+ "ix_k_sol_obs_transaction_observations_raw_transaction",
+ "ix_k_sol_obs_transaction_observations_received_at",
+ "ix_k_sol_obs_transaction_observations_signature",
+ "ix_k_sol_obs_transaction_observations_slot",
+ "ix_k_sol_obs_transaction_observations_status",
+ "ix_k_sol_ops_processing_ledger_input_hash",
+ "ix_k_sol_ops_processing_ledger_stage_input",
+ "ix_k_sol_ops_processing_ledger_status",
+ "ix_k_sol_raw_transactions_canonical_hash",
+ "ix_k_sol_raw_transactions_created_at",
+ "ix_k_sol_raw_transactions_processing",
+ "ix_k_sol_raw_transactions_processing_slot",
+ "ix_k_sol_raw_transactions_slot",
+ "pk_k_sol_core_account_keys",
+ "pk_k_sol_core_account_states",
+ "pk_k_sol_core_balance_changes",
+ "pk_k_sol_core_inner_instructions",
+ "pk_k_sol_core_instructions",
+ "pk_k_sol_core_logs",
+ "pk_k_sol_core_return_data",
+ "pk_k_sol_core_transactions",
+ "pk_k_sol_decode_coverage_declarations",
+ "pk_k_sol_decode_coverage_observations",
+ "pk_k_sol_decode_events",
+ "pk_k_sol_mat_outputs",
+ "pk_k_sol_obs_account_observations",
+ "pk_k_sol_obs_transaction_observations",
+ "pk_k_sol_ops_processing_ledger",
+ "pk_k_sol_raw_transactions",
+ "ux_k_sol_core_account_keys_sig_index",
+ "ux_k_sol_core_account_states_observation",
+ "ux_k_sol_core_balance_changes_sig_index",
+ "ux_k_sol_core_inner_instructions_sig_path",
+ "ux_k_sol_core_instructions_sig_path",
+ "ux_k_sol_core_logs_sig_index",
+ "ux_k_sol_core_return_data_signature",
+ "ux_k_sol_core_transactions_signature",
+ "ux_k_sol_decode_coverage_declarations_identity",
+ "ux_k_sol_decode_coverage_observations_identity",
+ "ux_k_sol_decode_events_processor_input_event",
+ "ux_k_sol_mat_outputs_processor_input_output",
+ "ux_k_sol_obs_account_observations_key",
+ "ux_k_sol_obs_transaction_observations_key",
+ "ux_k_sol_ops_processing_ledger_identity",
+ "ux_k_sol_raw_transactions_signature",
+];
+
+/// Physical PostgreSQL index names expected by the baseline, kept backend-private.
+pub(crate) fn expected_postgres_index_names() -> &'static [&'static str] {
+ return EXPECTED_POSTGRES_INDEX_NAMES;
+}
/// Diagnostic metadata for one expected PostgreSQL table.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -72,9 +169,10 @@ pub(crate) struct PostgresTableDiagnosticSpec {
pub role: &'static str,
}
-/// Diagnostic metadata for canonical transaction acquisition tables.
-pub(crate) fn raw_store_table_diagnostic_specs() -> [crate::PostgresTableDiagnosticSpec; 2] {
- return [
+/// Diagnostic metadata for raw store tables.
+pub(crate) fn raw_store_table_diagnostic_specs() -> std::vec::Vec
+{
+ return vec![
crate::PostgresTableDiagnosticSpec {
table_name: RAW_TRANSACTIONS_TABLE_NAME,
resource_code: "raw_transactions",
@@ -87,12 +185,19 @@ pub(crate) fn raw_store_table_diagnostic_specs() -> [crate::PostgresTableDiagnos
model_code: "raw",
role: "Transaction acquisition observations",
},
+ crate::PostgresTableDiagnosticSpec {
+ table_name: ACCOUNT_OBSERVATIONS_TABLE_NAME,
+ resource_code: "account_observations",
+ model_code: "raw",
+ role: "Account acquisition observations",
+ },
];
}
-/// Diagnostic metadata for core Solana store tables.
-pub(crate) fn core_store_table_diagnostic_specs() -> [crate::PostgresTableDiagnosticSpec; 7] {
- return [
+/// Diagnostic metadata for core store tables.
+pub(crate) fn core_store_table_diagnostic_specs()
+-> std::vec::Vec {
+ return vec![
crate::PostgresTableDiagnosticSpec {
table_name: CORE_TRANSACTIONS_TABLE_NAME,
resource_code: "transactions",
@@ -129,6 +234,18 @@ pub(crate) fn core_store_table_diagnostic_specs() -> [crate::PostgresTableDiagno
model_code: "core",
role: "Balance changes",
},
+ crate::PostgresTableDiagnosticSpec {
+ table_name: CORE_RETURN_DATA_TABLE_NAME,
+ resource_code: "return_data",
+ model_code: "core",
+ role: "Transaction return data",
+ },
+ crate::PostgresTableDiagnosticSpec {
+ table_name: CORE_ACCOUNT_STATES_TABLE_NAME,
+ resource_code: "account_states",
+ model_code: "core",
+ role: "Canonical account state observations",
+ },
crate::PostgresTableDiagnosticSpec {
table_name: PROCESSING_LEDGER_TABLE_NAME,
resource_code: "processing_ledger",
@@ -138,9 +255,10 @@ pub(crate) fn core_store_table_diagnostic_specs() -> [crate::PostgresTableDiagno
];
}
-/// Diagnostic metadata for decode and materialization store tables.
-pub(crate) fn decode_store_table_diagnostic_specs() -> [crate::PostgresTableDiagnosticSpec; 4] {
- return [
+/// Diagnostic metadata for decode store tables.
+pub(crate) fn decode_store_table_diagnostic_specs()
+-> std::vec::Vec {
+ return vec![
crate::PostgresTableDiagnosticSpec {
table_name: DECODE_EVENTS_TABLE_NAME,
resource_code: "decoded_observations",
@@ -160,7 +278,7 @@ pub(crate) fn decode_store_table_diagnostic_specs() -> [crate::PostgresTableDiag
role: "Observed decoder coverage",
},
crate::PostgresTableDiagnosticSpec {
- table_name: MATERIALIZED_EVENTS_TABLE_NAME,
+ table_name: MATERIALIZED_OUTPUTS_TABLE_NAME,
resource_code: "materialized_outputs",
model_code: "materialization",
role: "Versioned materialized outputs",
@@ -168,978 +286,1985 @@ pub(crate) fn decode_store_table_diagnostic_specs() -> [crate::PostgresTableDiag
];
}
-/// Idempotent SQL statements creating or upgrading the canonical transaction acquisition store.
+fn create_pg_table_if_not_exists_k_sol_core_account_keys() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_keys.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_account_states() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_account_states.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_balance_changes() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_balance_changes.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_inner_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_inner_instructions.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_instructions.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_logs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_logs.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_return_data() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_return_data.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_core_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_core_transactions.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_decode_coverage_declarations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_declarations.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_decode_coverage_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_coverage_observations.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_decode_events() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_decode_events.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_mat_outputs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_mat_outputs.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_obs_account_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_account_observations.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_obs_transaction_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_obs_transaction_observations.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_ops_processing_ledger() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_ops_processing_ledger.sql"
+ );
+}
+
+fn create_pg_table_if_not_exists_k_sol_raw_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_source() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_account_observations_status.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_account_states_observation() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_account_states_observation.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_instructions_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_instructions_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_logs_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_logs_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_return_data_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_return_data_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_account_keys() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_keys.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_account_states() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_account_states.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_balance_changes() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_balance_changes.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_inner_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_inner_instructions.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_instructions.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_logs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_logs.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_return_data() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_return_data.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_core_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_core_transactions.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_decode_coverage_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_coverage_observations.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_decode_events() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_decode_events.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_mat_outputs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_mat_outputs.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_obs_account_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_account_observations.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_obs_transaction_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_obs_transaction_observations.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_ops_processing_ledger() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_ops_processing_ledger.sql"
+ );
+}
+
+fn add_pg_constraint_if_not_exists_pk_k_sol_raw_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/constraints/add_constraint_if_not_exists_pk_k_sol_raw_transactions.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_account_keys_key() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_keys_key.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_account_states_account_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_account_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_account_states_owner() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_account_states_owner.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_account() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_account.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_mint() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_mint.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_owner() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_balance_changes_owner.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_parent() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_parent.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_processing() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_processing.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_program() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_inner_instructions_program.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_instructions_processing() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_processing.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_instructions_program() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_program.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_instructions_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_instructions_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_logs_path() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_path.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_logs_program() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_logs_program.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_return_data_program() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_return_data_program.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_transactions_created_at() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_created_at.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_core_transactions_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_core_transactions_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_events_family_commit() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_family_commit.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_events_program_surface() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_program_surface.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_decode_events_signature_path() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_decode_events_signature_path.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_mat_outputs_signature_family() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_mat_outputs_signature_family.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_received_at() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_received_at.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_account_observations_status.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction()
+-> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at() -> &'static str
+{
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_obs_transaction_observations_status.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_status() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_ops_processing_ledger_status.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_raw_transactions_created_at() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_created_at.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_raw_transactions_processing() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ix_k_sol_raw_transactions_slot() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_account_keys_sig_index() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_keys_sig_index.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_account_states_observation() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_account_states_observation.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_instructions_sig_path() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_instructions_sig_path.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_logs_sig_index() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_logs_sig_index.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_return_data_signature() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_return_data_signature.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_core_transactions_signature() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_core_transactions_signature.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_decode_events_processor_input_event() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_decode_events_processor_input_event.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_obs_account_observations_key() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_account_observations_key.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_obs_transaction_observations_key() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_obs_transaction_observations_key.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity.sql"
+ );
+}
+
+fn create_pg_index_if_not_exists_ux_k_sol_raw_transactions_signature() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/schema/indexes/create_index_if_not_exists_ux_k_sol_raw_transactions_signature.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_account_keys() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_account_states() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_balance_changes() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_inner_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_logs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_return_data() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_core_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_decode_coverage_declarations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_decode_coverage_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_decode_events() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_mat_outputs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_obs_account_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_obs_transaction_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_ops_processing_ledger() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql"
+ );
+}
+
+fn truncate_pg_table_k_sol_raw_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_account_keys() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_account_states() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_balance_changes() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_inner_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_instructions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_logs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_return_data() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_core_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_decode_coverage_declarations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_decode_coverage_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_decode_events() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_mat_outputs() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_obs_account_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_obs_transaction_observations() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_ops_processing_ledger() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql"
+ );
+}
+
+fn drop_pg_table_if_exists_k_sol_raw_transactions() -> &'static str {
+ return include_str!(
+ "../../migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql"
+ );
+}
+
+/// Idempotent PostgreSQL statements establishing the raw store schema.
pub(crate) fn raw_store_schema_statements() -> std::vec::Vec<&'static str> {
return vec![
- crate::postgres::migrations::migrate_legacy_raw_table_name_sql(),
- crate::postgres::migrations::migrate_legacy_raw_columns_sql(),
- crate::postgres::migrations::migrate_legacy_raw_constraints_sql(),
- crate::postgres::migrations::migrate_legacy_raw_indexes_sql(),
- crate::postgres::migrations::migrate_legacy_core_lineage_sql(),
- crate::postgres::migrations::create_table_kb_sol_raw_transactions_sql(),
- crate::postgres::migrations::create_ux_kb_sol_raw_transactions_signature_sql(),
- crate::postgres::migrations::create_ix_kb_sol_raw_transactions_slot_sql(),
- crate::postgres::migrations::create_ix_kb_sol_raw_transactions_created_at_sql(),
- crate::postgres::migrations::create_ix_kb_sol_raw_transactions_processing_sql(),
- crate::postgres::migrations::create_ix_kb_sol_raw_transactions_canonical_hash_sql(),
- crate::postgres::migrations::create_table_kb_sol_obs_transaction_observations_sql(),
- crate::postgres::migrations::create_ux_kb_sol_obs_transaction_observations_key_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_signature_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_slot_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_provider_method_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_received_at_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_raw_transaction_sql(),
- crate::postgres::migrations::create_ix_kb_sol_obs_transaction_observations_status_sql(),
- crate::postgres::migrations::migrate_and_drop_legacy_ws_notifications_sql(),
+ create_pg_table_if_not_exists_k_sol_obs_account_observations(),
+ create_pg_table_if_not_exists_k_sol_obs_transaction_observations(),
+ create_pg_table_if_not_exists_k_sol_raw_transactions(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_account_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_commitment_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_endpoint_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_code_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_error_message_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_filter_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_hash_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_key_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_lamports_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_method_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_origin(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_owner_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_payload_size_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_protocol_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_provider_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_rent_epoch_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_session_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_source_hash_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_space_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_state_shape(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_account_observations_status(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_commitment_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_endpoint_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_code_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_error_message_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_filter_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_hash_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_key_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_method_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_origin(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_payload_size_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_protocol_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_provider_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_session_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_obs_transaction_observations_status(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_format_version_positive(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_full_has_json(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_processing_state(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_retention_state(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_raw_transactions_slot_non_negative(),
+ add_pg_constraint_if_not_exists_pk_k_sol_obs_account_observations(),
+ add_pg_constraint_if_not_exists_pk_k_sol_obs_transaction_observations(),
+ add_pg_constraint_if_not_exists_pk_k_sol_raw_transactions(),
+ add_pg_constraint_if_not_exists_fk_k_sol_obs_transaction_observations_raw_transaction(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_account_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_provider_method(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_received_at(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_account_observations_status(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_provider_method(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_raw_transaction(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_received_at(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_signature(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_obs_transaction_observations_status(),
+ create_pg_index_if_not_exists_ix_k_sol_raw_transactions_canonical_hash(),
+ create_pg_index_if_not_exists_ix_k_sol_raw_transactions_created_at(),
+ create_pg_index_if_not_exists_ix_k_sol_raw_transactions_processing(),
+ create_pg_index_if_not_exists_ix_k_sol_raw_transactions_processing_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_raw_transactions_slot(),
+ create_pg_index_if_not_exists_ux_k_sol_obs_account_observations_key(),
+ create_pg_index_if_not_exists_ux_k_sol_obs_transaction_observations_key(),
+ create_pg_index_if_not_exists_ux_k_sol_raw_transactions_signature(),
];
}
-/// Idempotent SQL statements creating the minimal core Solana store.
-pub(crate) fn core_store_schema_statements() -> [&'static str; 28] {
- return [
- crate::postgres::migrations::create_table_kb_sol_core_transactions_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_transactions_signature_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_transactions_slot_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_transactions_created_at_sql(),
- crate::postgres::migrations::create_table_kb_sol_core_account_keys_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_account_keys_sig_index_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_account_keys_key_sql(),
- crate::postgres::migrations::create_table_kb_sol_core_instructions_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_instructions_sig_path_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_instructions_program_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_instructions_slot_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_instructions_processing_sql(),
- crate::postgres::migrations::create_table_kb_sol_core_inner_instructions_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_inner_instructions_sig_path_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_inner_instructions_parent_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_inner_instructions_program_sql(),
- crate::postgres::migrations::create_table_kb_sol_core_logs_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_logs_sig_index_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_logs_program_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_logs_path_sql(),
- crate::postgres::migrations::create_table_kb_sol_core_balance_changes_sql(),
- crate::postgres::migrations::create_ux_kb_sol_core_balance_changes_sig_index_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_balance_changes_account_sql(),
- crate::postgres::migrations::create_ix_kb_sol_core_balance_changes_mint_sql(),
- crate::postgres::migrations::create_table_kb_sol_ops_processing_ledger_sql(),
- crate::postgres::migrations::create_ux_kb_sol_ops_processing_ledger_identity_sql(),
- crate::postgres::migrations::create_ix_kb_sol_ops_processing_ledger_status_sql(),
- crate::postgres::migrations::create_ix_kb_sol_ops_processing_ledger_input_hash_sql(),
+/// Idempotent PostgreSQL statements establishing the core store schema.
+pub(crate) fn core_store_schema_statements() -> std::vec::Vec<&'static str> {
+ return vec![
+ create_pg_table_if_not_exists_k_sol_core_account_keys(),
+ create_pg_table_if_not_exists_k_sol_core_account_states(),
+ create_pg_table_if_not_exists_k_sol_core_balance_changes(),
+ create_pg_table_if_not_exists_k_sol_core_inner_instructions(),
+ create_pg_table_if_not_exists_k_sol_core_instructions(),
+ create_pg_table_if_not_exists_k_sol_core_logs(),
+ create_pg_table_if_not_exists_k_sol_core_return_data(),
+ create_pg_table_if_not_exists_k_sol_core_transactions(),
+ create_pg_table_if_not_exists_k_sol_ops_processing_ledger(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_keys_source(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processor_pair(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_program_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_inner_instructions_stack_height_positive(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_path_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_processing_state(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_processor_pair(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_program_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_instructions_stack_height_positive(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_logs_index_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_logs_path_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_logs_program_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_logs_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_logs_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_program_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_return_data_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_transactions_signature_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_core_transactions_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_attempt_count_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_code_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_error_message_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_hash_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_input_key_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_name_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_processor_version_not_empty(
+ ),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_stage_not_empty(),
+ add_pg_constraint_if_not_exists_ck_k_sol_ops_processing_ledger_status(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_account_keys(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_account_states(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_balance_changes(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_inner_instructions(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_instructions(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_logs(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_return_data(),
+ add_pg_constraint_if_not_exists_pk_k_sol_core_transactions(),
+ add_pg_constraint_if_not_exists_pk_k_sol_ops_processing_ledger(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_account_keys_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_account_states_observation(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_balance_changes_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_inner_instructions_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_instructions_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_logs_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_return_data_transaction(),
+ add_pg_constraint_if_not_exists_fk_k_sol_core_transactions_raw_transaction(),
+ create_pg_index_if_not_exists_ix_k_sol_core_account_keys_key(),
+ create_pg_index_if_not_exists_ix_k_sol_core_account_states_account_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_core_account_states_owner(),
+ create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_account(),
+ create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_mint(),
+ create_pg_index_if_not_exists_ix_k_sol_core_balance_changes_owner(),
+ create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_parent(),
+ create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_processing(),
+ create_pg_index_if_not_exists_ix_k_sol_core_inner_instructions_program(),
+ create_pg_index_if_not_exists_ix_k_sol_core_instructions_processing(),
+ create_pg_index_if_not_exists_ix_k_sol_core_instructions_program(),
+ create_pg_index_if_not_exists_ix_k_sol_core_instructions_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_core_logs_path(),
+ create_pg_index_if_not_exists_ix_k_sol_core_logs_program(),
+ create_pg_index_if_not_exists_ix_k_sol_core_return_data_program(),
+ create_pg_index_if_not_exists_ix_k_sol_core_transactions_created_at(),
+ create_pg_index_if_not_exists_ix_k_sol_core_transactions_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_input_hash(),
+ create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_stage_input(),
+ create_pg_index_if_not_exists_ix_k_sol_ops_processing_ledger_status(),
+ create_pg_index_if_not_exists_ux_k_sol_core_account_keys_sig_index(),
+ create_pg_index_if_not_exists_ux_k_sol_core_account_states_observation(),
+ create_pg_index_if_not_exists_ux_k_sol_core_balance_changes_sig_index(),
+ create_pg_index_if_not_exists_ux_k_sol_core_inner_instructions_sig_path(),
+ create_pg_index_if_not_exists_ux_k_sol_core_instructions_sig_path(),
+ create_pg_index_if_not_exists_ux_k_sol_core_logs_sig_index(),
+ create_pg_index_if_not_exists_ux_k_sol_core_return_data_signature(),
+ create_pg_index_if_not_exists_ux_k_sol_core_transactions_signature(),
+ create_pg_index_if_not_exists_ux_k_sol_ops_processing_ledger_identity(),
];
}
-/// Idempotent SQL statements creating the common decode and materialization store.
-pub(crate) fn decode_store_schema_statements() -> [&'static str; 15] {
- return [
- crate::postgres::migrations::create_table_kb_sol_decode_events_sql(),
- crate::postgres::migrations::create_ux_kb_sol_decode_events_identity_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_events_signature_path_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_events_program_surface_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_events_family_commit_sql(),
- crate::postgres::migrations::create_table_kb_sol_decode_coverage_declarations_sql(),
- crate::postgres::migrations::create_ux_kb_sol_decode_coverage_declarations_identity_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_coverage_declarations_program_sql(),
- crate::postgres::migrations::create_table_kb_sol_decode_coverage_observations_sql(),
- crate::postgres::migrations::create_ux_kb_sol_decode_coverage_observations_identity_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_coverage_observations_program_status_sql(),
- crate::postgres::migrations::create_ix_kb_sol_decode_coverage_observations_entry_sql(),
- crate::postgres::migrations::create_table_kb_sol_mat_events_sql(),
- crate::postgres::migrations::create_ux_kb_sol_mat_events_identity_sql(),
- crate::postgres::migrations::create_ix_kb_sol_mat_events_signature_family_sql(),
+/// Idempotent PostgreSQL statements establishing the decode store schema.
+pub(crate) fn decode_store_schema_statements() -> std::vec::Vec<&'static str> {
+ return vec![
+ create_pg_table_if_not_exists_k_sol_decode_coverage_declarations(),
+ create_pg_table_if_not_exists_k_sol_decode_coverage_observations(),
+ create_pg_table_if_not_exists_k_sol_decode_events(),
+ create_pg_table_if_not_exists_k_sol_mat_outputs(),
+ add_pg_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_counts_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_decode_coverage_observations_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_decode_events_failed_not_committed(),
+ add_pg_constraint_if_not_exists_ck_k_sol_decode_events_schema_provenance(),
+ add_pg_constraint_if_not_exists_ck_k_sol_decode_events_slot_non_negative(),
+ add_pg_constraint_if_not_exists_ck_k_sol_mat_outputs_slot_non_negative(),
+ add_pg_constraint_if_not_exists_pk_k_sol_decode_coverage_declarations(),
+ add_pg_constraint_if_not_exists_pk_k_sol_decode_coverage_observations(),
+ add_pg_constraint_if_not_exists_pk_k_sol_decode_events(),
+ add_pg_constraint_if_not_exists_pk_k_sol_mat_outputs(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_coverage_declarations_program(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_coverage_observations_entry(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_coverage_observations_program_status(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_events_family_commit(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_events_program_surface(),
+ create_pg_index_if_not_exists_ix_k_sol_decode_events_signature_path(),
+ create_pg_index_if_not_exists_ix_k_sol_mat_outputs_processor_family_slot(),
+ create_pg_index_if_not_exists_ix_k_sol_mat_outputs_signature_family(),
+ create_pg_index_if_not_exists_ux_k_sol_decode_coverage_declarations_identity(),
+ create_pg_index_if_not_exists_ux_k_sol_decode_coverage_observations_identity(),
+ create_pg_index_if_not_exists_ux_k_sol_decode_events_processor_input_event(),
+ create_pg_index_if_not_exists_ux_k_sol_mat_outputs_processor_input_output(),
];
}
-/// SQL conditionally renaming the historical raw RPC transaction table.
-fn migrate_legacy_raw_table_name_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('kb_sol_raw_rpc_transactions') IS NOT NULL
- AND to_regclass('kb_sol_raw_transactions') IS NULL THEN
- ALTER TABLE kb_sol_raw_rpc_transactions RENAME TO kb_sol_raw_transactions;
- ELSIF to_regclass('kb_sol_raw_rpc_transactions') IS NOT NULL
- AND to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN
- RAISE EXCEPTION 'both legacy and canonical raw transaction tables exist';
- END IF;
-END
-$$"#;
+fn truncate_store_schema_statements() -> std::vec::Vec<&'static str> {
+ return vec![
+ truncate_pg_table_k_sol_core_account_keys(),
+ truncate_pg_table_k_sol_core_account_states(),
+ truncate_pg_table_k_sol_core_balance_changes(),
+ truncate_pg_table_k_sol_core_inner_instructions(),
+ truncate_pg_table_k_sol_core_instructions(),
+ truncate_pg_table_k_sol_core_logs(),
+ truncate_pg_table_k_sol_core_return_data(),
+ truncate_pg_table_k_sol_core_transactions(),
+ truncate_pg_table_k_sol_decode_coverage_declarations(),
+ truncate_pg_table_k_sol_decode_coverage_observations(),
+ truncate_pg_table_k_sol_decode_events(),
+ truncate_pg_table_k_sol_mat_outputs(),
+ truncate_pg_table_k_sol_obs_account_observations(),
+ truncate_pg_table_k_sol_obs_transaction_observations(),
+ truncate_pg_table_k_sol_ops_processing_ledger(),
+ truncate_pg_table_k_sol_raw_transactions(),
+ ];
}
-/// SQL conditionally renaming historical raw payload columns and adding the format version.
-fn migrate_legacy_raw_columns_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN
- IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'raw_json')
- AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'canonical_json') THEN
- ALTER TABLE kb_sol_raw_transactions RENAME COLUMN raw_json TO canonical_json;
- END IF;
- IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'raw_json_hash')
- AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'canonical_json_hash') THEN
- ALTER TABLE kb_sol_raw_transactions RENAME COLUMN raw_json_hash TO canonical_json_hash;
- END IF;
- ALTER TABLE kb_sol_raw_transactions ADD COLUMN IF NOT EXISTS canonical_format_version INTEGER NOT NULL DEFAULT 1;
- END IF;
-END
-$$"#;
+fn drop_store_schema_statements() -> std::vec::Vec<&'static str> {
+ return vec![
+ drop_pg_table_if_exists_k_sol_core_account_keys(),
+ drop_pg_table_if_exists_k_sol_core_account_states(),
+ drop_pg_table_if_exists_k_sol_core_balance_changes(),
+ drop_pg_table_if_exists_k_sol_core_inner_instructions(),
+ drop_pg_table_if_exists_k_sol_core_instructions(),
+ drop_pg_table_if_exists_k_sol_core_logs(),
+ drop_pg_table_if_exists_k_sol_core_return_data(),
+ drop_pg_table_if_exists_k_sol_core_transactions(),
+ drop_pg_table_if_exists_k_sol_decode_coverage_declarations(),
+ drop_pg_table_if_exists_k_sol_decode_coverage_observations(),
+ drop_pg_table_if_exists_k_sol_decode_events(),
+ drop_pg_table_if_exists_k_sol_mat_outputs(),
+ drop_pg_table_if_exists_k_sol_obs_account_observations(),
+ drop_pg_table_if_exists_k_sol_obs_transaction_observations(),
+ drop_pg_table_if_exists_k_sol_ops_processing_ledger(),
+ drop_pg_table_if_exists_k_sol_raw_transactions(),
+ ];
}
-/// SQL conditionally renaming historical canonical transaction constraints.
-fn migrate_legacy_raw_constraints_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'pk_kb_sol_raw_rpc_transactions') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT pk_kb_sol_raw_rpc_transactions TO pk_kb_sol_raw_transactions; END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_signature_not_empty') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_signature_not_empty TO ck_kb_sol_raw_transactions_signature_not_empty; END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_slot_non_negative') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_slot_non_negative TO ck_kb_sol_raw_transactions_slot_non_negative; END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_retention_state') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_retention_state TO ck_kb_sol_raw_transactions_retention_state; END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_processing_state') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_processing_state TO ck_kb_sol_raw_transactions_processing_state; END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_full_has_json') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_full_has_json TO ck_kb_sol_raw_transactions_full_has_json; END IF;
- IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_transactions_format_version_positive') THEN ALTER TABLE kb_sol_raw_transactions ADD CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0); END IF;
- END IF;
-END
-$$"#;
-}
-
-/// SQL conditionally renaming historical canonical transaction indexes.
-fn migrate_legacy_raw_indexes_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('ux_kb_sol_raw_rpc_transactions_signature') IS NOT NULL AND to_regclass('ux_kb_sol_raw_transactions_signature') IS NULL THEN ALTER INDEX ux_kb_sol_raw_rpc_transactions_signature RENAME TO ux_kb_sol_raw_transactions_signature; END IF;
- IF to_regclass('ix_kb_sol_raw_rpc_transactions_slot') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_slot') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_slot RENAME TO ix_kb_sol_raw_transactions_slot; END IF;
- IF to_regclass('ix_kb_sol_raw_rpc_transactions_created_at') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_created_at') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_created_at RENAME TO ix_kb_sol_raw_transactions_created_at; END IF;
- IF to_regclass('ix_kb_sol_raw_rpc_transactions_processing') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_processing') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_processing RENAME TO ix_kb_sol_raw_transactions_processing; END IF;
-END
-$$"#;
-}
-
-/// SQL conditionally renaming historical core-to-raw lineage.
-fn migrate_legacy_core_lineage_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('kb_sol_core_transactions') IS NOT NULL THEN
- IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_core_transactions' AND column_name = 'raw_rpc_transaction_id')
- AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_core_transactions' AND column_name = 'raw_transaction_id') THEN
- ALTER TABLE kb_sol_core_transactions RENAME COLUMN raw_rpc_transaction_id TO raw_transaction_id;
- END IF;
- IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_core_transactions'::regclass AND conname = 'fk_kb_sol_core_transactions_raw_rpc_transaction') THEN
- ALTER TABLE kb_sol_core_transactions RENAME CONSTRAINT fk_kb_sol_core_transactions_raw_rpc_transaction TO fk_kb_sol_core_transactions_raw_transaction;
- END IF;
- END IF;
-END
-$$"#;
-}
-
-/// SQL creating `kb_sol_raw_transactions`.
-fn create_table_kb_sol_raw_transactions_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_raw_transactions (
- id BIGSERIAL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- canonical_json JSONB,
- canonical_json_hash TEXT,
- canonical_format_version INTEGER NOT NULL DEFAULT 1,
- retention_state TEXT NOT NULL DEFAULT 'full',
- processing_state TEXT NOT NULL DEFAULT 'received',
- lifecycle_reason TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_raw_transactions PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_raw_transactions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0),
- CONSTRAINT ck_kb_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged')),
- CONSTRAINT ck_kb_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed')),
- CONSTRAINT ck_kb_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL)
- )"#;
-}
-
-/// SQL creating the unique signature index for canonical raw transactions.
-fn create_ux_kb_sol_raw_transactions_signature_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_raw_transactions_signature ON kb_sol_raw_transactions (signature)";
-}
-
-/// SQL creating the slot index for canonical raw transactions.
-fn create_ix_kb_sol_raw_transactions_slot_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_slot ON kb_sol_raw_transactions (slot)";
-}
-
-/// SQL creating the created timestamp index for canonical raw transactions.
-fn create_ix_kb_sol_raw_transactions_created_at_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_created_at ON kb_sol_raw_transactions (created_at)";
-}
-
-/// SQL creating the processing state index for canonical raw transactions.
-fn create_ix_kb_sol_raw_transactions_processing_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_processing ON kb_sol_raw_transactions (processing_state)";
-}
-
-/// SQL creating the optional canonical document hash index.
-fn create_ix_kb_sol_raw_transactions_canonical_hash_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_canonical_hash ON kb_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL";
-}
-
-/// SQL creating `kb_sol_obs_transaction_observations`.
-fn create_table_kb_sol_obs_transaction_observations_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_obs_transaction_observations (
- id BIGSERIAL,
- raw_transaction_id BIGINT,
- observation_key TEXT NOT NULL,
- signature TEXT,
- slot BIGINT,
- provider TEXT NOT NULL,
- endpoint_code TEXT,
- protocol TEXT NOT NULL,
- acquisition_method TEXT NOT NULL,
- origin TEXT NOT NULL,
- commitment TEXT,
- capture_session_id TEXT,
- filter_code TEXT,
- detected_at TIMESTAMPTZ,
- received_at TIMESTAMPTZ NOT NULL,
- normalized_at TIMESTAMPTZ,
- persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- payload_size_bytes BIGINT,
- source_payload_hash TEXT,
- status TEXT NOT NULL DEFAULT 'received',
- error_code TEXT,
- error_message TEXT,
- CONSTRAINT pk_kb_sol_obs_transaction_observations PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
- CONSTRAINT ck_kb_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration')),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing')),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
- CONSTRAINT ck_kb_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
- )"#;
-}
-
-/// SQL creating the unique observation key index.
-fn create_ux_kb_sol_obs_transaction_observations_key_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_obs_transaction_observations_key ON kb_sol_obs_transaction_observations (observation_key)";
-}
-
-/// SQL creating the optional signature index for transaction observations.
-fn create_ix_kb_sol_obs_transaction_observations_signature_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_signature ON kb_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL";
-}
-
-/// SQL creating the optional slot index for transaction observations.
-fn create_ix_kb_sol_obs_transaction_observations_slot_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_slot ON kb_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL";
-}
-
-/// SQL creating the provider and acquisition method index for transaction observations.
-fn create_ix_kb_sol_obs_transaction_observations_provider_method_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_provider_method ON kb_sol_obs_transaction_observations (provider, acquisition_method)";
-}
-
-/// SQL creating the received timestamp index for transaction observations.
-fn create_ix_kb_sol_obs_transaction_observations_received_at_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_received_at ON kb_sol_obs_transaction_observations (received_at)";
-}
-
-/// SQL creating the optional canonical transaction lineage index for observations.
-fn create_ix_kb_sol_obs_transaction_observations_raw_transaction_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_raw_transaction ON kb_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL";
-}
-
-/// SQL creating the status index for transaction observations.
-fn create_ix_kb_sol_obs_transaction_observations_status_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_status ON kb_sol_obs_transaction_observations (status)";
-}
-
-/// SQL migrating lightweight metadata and dropping the historical WebSocket payload table.
-fn migrate_and_drop_legacy_ws_notifications_sql() -> &'static str {
- return r#"DO $$
-BEGIN
- IF to_regclass('kb_sol_raw_ws_notifications') IS NOT NULL THEN
- INSERT INTO kb_sol_obs_transaction_observations (raw_transaction_id, observation_key, signature, slot, provider, protocol, acquisition_method, origin, received_at, persisted_at, payload_size_bytes, source_payload_hash, status, error_message)
- SELECT linked_raw_rpc_transaction_id, notification_key, signature, slot, 'legacy_unknown', 'solana_websocket', subscription_kind, 'migration', created_at, created_at, CASE WHEN raw_json IS NULL THEN NULL ELSE octet_length(convert_to(raw_json::text, 'UTF8'))::BIGINT END, NULLIF(btrim(raw_json_hash), ''), CASE WHEN processing_state = 'failed' THEN 'failed' ELSE 'received' END, NULLIF(btrim(lifecycle_reason), '')
- FROM kb_sol_raw_ws_notifications
- ON CONFLICT (observation_key) DO NOTHING;
- DROP TABLE kb_sol_raw_ws_notifications;
- END IF;
-END
-$$"#;
-}
-
-/// SQL creating `kb_sol_core_transactions`.
-fn create_table_kb_sol_core_transactions_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_transactions (
- id BIGSERIAL,
- raw_transaction_id BIGINT,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- failed BOOLEAN NOT NULL DEFAULT FALSE,
- err_json JSONB,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_transactions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
- CONSTRAINT ck_kb_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_transactions_slot_non_negative CHECK (slot >= 0)
- )"#;
-}
-
-/// SQL creating the unique signature index for core transactions.
-fn create_ux_kb_sol_core_transactions_signature_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_transactions_signature ON kb_sol_core_transactions (signature)";
-}
-
-/// SQL creating the slot index for core transactions.
-fn create_ix_kb_sol_core_transactions_slot_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_slot ON kb_sol_core_transactions (slot)";
-}
-
-/// SQL creating the created_at index for core transactions.
-fn create_ix_kb_sol_core_transactions_created_at_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_created_at ON kb_sol_core_transactions (created_at)";
-}
-
-/// SQL creating `kb_sol_core_account_keys`.
-fn create_table_kb_sol_core_account_keys_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_account_keys (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- account_index INTEGER NOT NULL,
- account_key TEXT NOT NULL,
- source TEXT NOT NULL,
- writable BOOLEAN NOT NULL,
- signer BOOLEAN NOT NULL,
- executable BOOLEAN,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_account_keys PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_account_keys_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_account_keys_index_non_negative CHECK (account_index >= 0),
- CONSTRAINT ck_kb_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0),
- CONSTRAINT ck_kb_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly'))
- )"#;
-}
-
-/// SQL creating the unique signature/account index for core account keys.
-fn create_ux_kb_sol_core_account_keys_sig_index_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_account_keys_sig_index ON kb_sol_core_account_keys (signature, account_index)";
-}
-
-/// SQL creating the account key index for core account keys.
-fn create_ix_kb_sol_core_account_keys_key_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_account_keys_key ON kb_sol_core_account_keys (account_key)";
-}
-
-/// SQL creating `kb_sol_core_instructions`.
-fn create_table_kb_sol_core_instructions_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_instructions (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- accounts_json JSONB NOT NULL,
- payload_json JSONB,
- payload_json_hash TEXT,
- processing_state TEXT NOT NULL DEFAULT 'pending',
- processor_name TEXT,
- processor_version TEXT,
- lifecycle_reason TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_instructions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0),
- CONSTRAINT ck_kb_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'))
- )"#;
-}
-
-/// SQL creating the unique signature/path index for core instructions.
-fn create_ux_kb_sol_core_instructions_sig_path_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_instructions_sig_path ON kb_sol_core_instructions (signature, instruction_path)";
-}
-
-/// SQL creating the program index for core instructions.
-fn create_ix_kb_sol_core_instructions_program_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_program ON kb_sol_core_instructions (program_id)";
-}
-
-/// SQL creating the slot index for core instructions.
-fn create_ix_kb_sol_core_instructions_slot_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_slot ON kb_sol_core_instructions (slot)";
-}
-
-/// SQL creating the processing state index for core instructions.
-fn create_ix_kb_sol_core_instructions_processing_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_processing ON kb_sol_core_instructions (processing_state)";
-}
-
-/// SQL creating `kb_sol_core_inner_instructions`.
-fn create_table_kb_sol_core_inner_instructions_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_inner_instructions (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- parent_instruction_path TEXT NOT NULL,
- instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL,
- accounts_json JSONB NOT NULL,
- payload_json JSONB,
- payload_json_hash TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_inner_instructions PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0)
- )"#;
-}
-
-/// SQL creating the unique signature/path index for core inner instructions.
-fn create_ux_kb_sol_core_inner_instructions_sig_path_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_inner_instructions_sig_path ON kb_sol_core_inner_instructions (signature, instruction_path)";
-}
-
-/// SQL creating the parent instruction index for core inner instructions.
-fn create_ix_kb_sol_core_inner_instructions_parent_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_parent ON kb_sol_core_inner_instructions (signature, parent_instruction_path)";
-}
-
-/// SQL creating the program index for core inner instructions.
-fn create_ix_kb_sol_core_inner_instructions_program_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_program ON kb_sol_core_inner_instructions (program_id)";
-}
-
-/// SQL creating `kb_sol_core_logs`.
-fn create_table_kb_sol_core_logs_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_logs (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- log_index INTEGER NOT NULL,
- instruction_path TEXT,
- program_id TEXT,
- log_text TEXT,
- log_text_hash TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_logs PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_logs_index_non_negative CHECK (log_index >= 0),
- CONSTRAINT ck_kb_sol_core_logs_text_not_empty CHECK (log_text IS NULL OR length(btrim(log_text)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0),
- CONSTRAINT ck_kb_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0)
- )"#;
-}
-
-/// SQL creating the unique signature/log index for core logs.
-fn create_ux_kb_sol_core_logs_sig_index_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_logs_sig_index ON kb_sol_core_logs (signature, log_index)";
-}
-
-/// SQL creating the program index for core logs.
-fn create_ix_kb_sol_core_logs_program_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_program ON kb_sol_core_logs (program_id) WHERE program_id IS NOT NULL";
-}
-
-/// SQL creating the instruction path index for core logs.
-fn create_ix_kb_sol_core_logs_path_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_path ON kb_sol_core_logs (signature, instruction_path) WHERE instruction_path IS NOT NULL";
-}
-
-/// SQL creating `kb_sol_core_balance_changes`.
-fn create_table_kb_sol_core_balance_changes_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_balance_changes (
- id BIGSERIAL,
- transaction_id BIGINT NOT NULL,
- signature TEXT NOT NULL,
- slot BIGINT NOT NULL,
- balance_change_index INTEGER NOT NULL,
- balance_kind TEXT NOT NULL,
- account_index INTEGER,
- account_key TEXT,
- mint TEXT,
- owner TEXT,
- pre_balance_json JSONB,
- post_balance_json JSONB,
- delta_json JSONB,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_core_balance_changes PRIMARY KEY (id),
- CONSTRAINT fk_kb_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
- CONSTRAINT ck_kb_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount')),
- CONSTRAINT ck_kb_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0),
- CONSTRAINT ck_kb_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0)
- )"#;
-}
-
-/// SQL creating the unique signature/balance index for core balance changes.
-fn create_ux_kb_sol_core_balance_changes_sig_index_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_balance_changes_sig_index ON kb_sol_core_balance_changes (signature, balance_change_index)";
-}
-
-/// SQL creating the account key index for core balance changes.
-fn create_ix_kb_sol_core_balance_changes_account_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_account ON kb_sol_core_balance_changes (account_key) WHERE account_key IS NOT NULL";
-}
-
-/// SQL creating the mint index for core balance changes.
-fn create_ix_kb_sol_core_balance_changes_mint_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_mint ON kb_sol_core_balance_changes (mint) WHERE mint IS NOT NULL";
-}
-
-/// Returns true when a Solana table name follows the canonical prefix and domain rules.
-#[cfg(test)]
-fn is_valid_solana_table_name(table_name: &str) -> bool {
- return validate_solana_table_name(table_name).is_ok();
-}
-
-/// Validates a canonical Solana table name.
-fn validate_solana_table_name(table_name: &str) -> ks_core::Result<()> {
- let trimmed_table_name = table_name.trim();
- if trimmed_table_name.is_empty() {
- return std::result::Result::Err(ks_core::Error::db("table name must not be empty"));
- }
- if trimmed_table_name.contains('.') {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table names must not contain an explicit PostgreSQL schema",
- ));
- }
- if !trimmed_table_name.starts_with(SOLANA_TABLE_PREFIX) {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table name must start with kb_sol_",
- ));
- }
- let suffix = &trimmed_table_name[SOLANA_TABLE_PREFIX.len()..];
- let domain = crate::postgres::migrations::first_segment(suffix);
- if domain.is_empty() {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table name must include a domain after kb_sol_",
- ));
- }
- if !crate::postgres::migrations::is_allowed_domain(domain) {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table name domain is not allowed",
- ));
- }
- if suffix.len() <= domain.len() + 1 {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table name must include a name after the domain",
- ));
- }
- if !crate::postgres::migrations::contains_only_table_name_chars(trimmed_table_name) {
- return std::result::Result::Err(ks_core::Error::db(
- "solana table name must use lowercase ascii, digits and underscores only",
- ));
+/// Validates that every embedded PostgreSQL schema and maintenance resource is non-empty and atomic.
+pub(crate) fn validate_postgres_sql_resources() -> ks_core::Result<()> {
+ let groups = [
+ raw_store_schema_statements(),
+ core_store_schema_statements(),
+ decode_store_schema_statements(),
+ truncate_store_schema_statements(),
+ drop_store_schema_statements(),
+ ];
+ for group in groups {
+ for statement in group {
+ let trimmed = strip_sql_resource_header(statement);
+ if trimmed.trim().is_empty() {
+ return std::result::Result::Err(ks_core::Error::db(
+ "postgres SQL resource must not be empty",
+ ));
+ }
+ if contains_multiple_top_level_statements(trimmed) {
+ return std::result::Result::Err(ks_core::Error::db(
+ "postgres SQL resource must contain exactly one top-level statement",
+ ));
+ }
+ }
}
return std::result::Result::Ok(());
}
-/// Validates the raw store table names introduced by `0.2.3`.
-pub(crate) fn validate_raw_store_table_names() -> ks_core::Result<()> {
- return crate::postgres::migrations::validate_table_names(RAW_STORE_TABLE_NAMES);
+fn strip_sql_resource_header(sql: &str) -> &str {
+ let mut offset = 0_usize;
+ for line in sql.split_inclusive('\n') {
+ let trimmed = line.trim();
+ if trimmed.is_empty() || trimmed.starts_with("--") {
+ offset += line.len();
+ continue;
+ }
+ break;
+ }
+ return &sql[offset..];
}
-/// Validates the core store table names introduced by `0.2.4`.
-pub(crate) fn validate_core_store_table_names() -> ks_core::Result<()> {
- return crate::postgres::migrations::validate_table_names(CORE_STORE_TABLE_NAMES);
+fn contains_multiple_top_level_statements(sql: &str) -> bool {
+ let trimmed = sql.trim();
+ if trimmed.starts_with("DO $$") {
+ return !trimmed.ends_with("$$;");
+ }
+ let without_last = match trimmed.strip_suffix(';') {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => trimmed,
+ };
+ return without_last.contains(';');
}
-/// Validates the decode and materialization table names introduced by `0.4.0`.
-pub(crate) fn validate_decode_store_table_names() -> ks_core::Result<()> {
- return crate::postgres::migrations::validate_table_names(DECODE_STORE_TABLE_NAMES);
+#[cfg(test)]
+fn validate_raw_store_table_names() -> ks_core::Result<()> {
+ return validate_table_names(RAW_STORE_TABLE_NAMES);
+}
+#[cfg(test)]
+fn validate_core_store_table_names() -> ks_core::Result<()> {
+ return validate_table_names(CORE_STORE_TABLE_NAMES);
+}
+#[cfg(test)]
+fn validate_decode_store_table_names() -> ks_core::Result<()> {
+ return validate_table_names(DECODE_STORE_TABLE_NAMES);
}
+#[cfg(test)]
fn validate_table_names(table_names: &[&str]) -> ks_core::Result<()> {
for table_name in table_names {
- let validation_result = validate_solana_table_name(table_name);
- if let std::result::Result::Err(error) = validation_result {
- return std::result::Result::Err(error);
+ if !is_valid_solana_table_name(table_name) {
+ return std::result::Result::Err(ks_core::Error::db(format!(
+ "invalid Solana table name: {table_name}"
+ )));
}
}
return std::result::Result::Ok(());
}
-fn first_segment(value: &str) -> &str {
- return match value.split_once('_') {
- std::option::Option::Some((first, _rest)) => first,
- std::option::Option::None => value,
- };
-}
-
-fn is_allowed_domain(domain: &str) -> bool {
- for allowed_domain in ALLOWED_SOLANA_TABLE_DOMAINS {
- if domain == *allowed_domain {
- return true;
- }
- }
- return false;
-}
-
-fn contains_only_table_name_chars(table_name: &str) -> bool {
- for character in table_name.chars() {
- if character == '_' {
- continue;
- }
- if character.is_ascii_lowercase() {
- continue;
- }
- if character.is_ascii_digit() {
- continue;
- }
+#[cfg(test)]
+fn is_valid_solana_table_name(table_name: &str) -> bool {
+ if table_name.contains('.')
+ || !table_name.starts_with(SOLANA_TABLE_PREFIX)
+ || table_name.chars().any(|value| return value.is_ascii_uppercase())
+ {
return false;
}
- return true;
+ let suffix = &table_name[SOLANA_TABLE_PREFIX.len()..];
+ let split = suffix.split_once('_');
+ let (domain, remainder) = match split {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => return false,
+ };
+ return ALLOWED_SOLANA_TABLE_DOMAINS.contains(&domain) && !remainder.trim().is_empty();
}
-/// SQL creating `kb_sol_ops_processing_ledger`.
-fn create_table_kb_sol_ops_processing_ledger_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_ops_processing_ledger (
- id BIGSERIAL,
- stage TEXT NOT NULL,
- processor_name TEXT NOT NULL,
- processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL,
- input_hash TEXT NOT NULL,
- status TEXT NOT NULL,
- attempt_count INTEGER NOT NULL DEFAULT 0,
- started_at TIMESTAMPTZ,
- finished_at TIMESTAMPTZ,
- error_code TEXT,
- error_message TEXT,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_ops_processing_ledger PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed')),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
- CONSTRAINT ck_kb_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
- )"#;
+pub(crate) fn table_stats_k_sol_raw_transactions_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_raw_transactions";
+}
+pub(crate) fn table_stats_k_sol_obs_transaction_observations_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(persisted_at)::TEXT AS latest_created_at FROM k_sol_obs_transaction_observations";
+}
+pub(crate) fn table_stats_k_sol_obs_account_observations_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(context_slot)::BIGINT AS min_slot, MAX(context_slot)::BIGINT AS max_slot, MAX(persisted_at)::TEXT AS latest_created_at FROM k_sol_obs_account_observations";
+}
+pub(crate) fn table_stats_k_sol_core_transactions_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_transactions";
+}
+pub(crate) fn table_stats_k_sol_core_account_keys_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_account_keys";
+}
+pub(crate) fn table_stats_k_sol_core_instructions_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_instructions";
+}
+pub(crate) fn table_stats_k_sol_core_inner_instructions_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_inner_instructions";
+}
+pub(crate) fn table_stats_k_sol_core_logs_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_logs";
+}
+pub(crate) fn table_stats_k_sol_core_balance_changes_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_balance_changes";
+}
+pub(crate) fn table_stats_k_sol_core_return_data_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_return_data";
+}
+pub(crate) fn table_stats_k_sol_core_account_states_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_core_account_states";
+}
+pub(crate) fn table_stats_k_sol_ops_processing_ledger_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_ops_processing_ledger";
+}
+pub(crate) fn table_stats_k_sol_decode_events_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_decode_events";
+}
+pub(crate) fn table_stats_k_sol_decode_coverage_declarations_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_decode_coverage_declarations";
+}
+pub(crate) fn table_stats_k_sol_decode_coverage_observations_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_decode_coverage_observations";
+}
+pub(crate) fn table_stats_k_sol_mat_outputs_sql() -> &'static str {
+ return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot)::BIGINT AS min_slot, MAX(slot)::BIGINT AS max_slot, MAX(created_at)::TEXT AS latest_created_at FROM k_sol_mat_outputs";
}
-/// SQL creating the unique processing ledger identity index.
-fn create_ux_kb_sol_ops_processing_ledger_identity_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_ops_processing_ledger_identity ON kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key)";
-}
-
-/// SQL creating the processing ledger status index.
-fn create_ix_kb_sol_ops_processing_ledger_status_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_status ON kb_sol_ops_processing_ledger (stage, processor_name, status, updated_at)";
-}
-
-/// SQL creating the processing ledger input hash index.
-fn create_ix_kb_sol_ops_processing_ledger_input_hash_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_input_hash ON kb_sol_ops_processing_ledger (input_hash)";
-}
-
-/// SQL statistics query for `kb_sol_raw_transactions`.
-pub(crate) fn table_stats_kb_sol_raw_transactions_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_raw_transactions";
-}
-
-/// SQL statistics query for `kb_sol_obs_transaction_observations`.
-pub(crate) fn table_stats_kb_sol_obs_transaction_observations_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(persisted_at)::text AS latest_created_at FROM kb_sol_obs_transaction_observations";
-}
-
-/// SQL statistics query for `kb_sol_core_transactions`.
-pub(crate) fn table_stats_kb_sol_core_transactions_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_transactions";
-}
-
-/// SQL statistics query for `kb_sol_core_account_keys`.
-pub(crate) fn table_stats_kb_sol_core_account_keys_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_account_keys";
-}
-
-/// SQL statistics query for `kb_sol_core_instructions`.
-pub(crate) fn table_stats_kb_sol_core_instructions_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_instructions";
-}
-
-/// SQL statistics query for `kb_sol_core_inner_instructions`.
-pub(crate) fn table_stats_kb_sol_core_inner_instructions_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_inner_instructions";
-}
-
-/// SQL statistics query for `kb_sol_core_logs`.
-pub(crate) fn table_stats_kb_sol_core_logs_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_logs";
-}
-
-/// SQL statistics query for `kb_sol_core_balance_changes`.
-pub(crate) fn table_stats_kb_sol_core_balance_changes_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_balance_changes";
-}
-
-/// SQL statistics query for `kb_sol_ops_processing_ledger`.
-pub(crate) fn table_stats_kb_sol_ops_processing_ledger_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(updated_at)::text AS latest_created_at FROM kb_sol_ops_processing_ledger";
-}
-
-/// SQL statistics query for `kb_sol_decode_events`.
-pub(crate) fn table_stats_kb_sol_decode_events_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_events";
-}
-
-/// SQL statistics query for `kb_sol_decode_coverage_declarations`.
-pub(crate) fn table_stats_kb_sol_decode_coverage_declarations_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_coverage_declarations";
-}
-
-/// SQL statistics query for `kb_sol_decode_coverage_observations`.
-pub(crate) fn table_stats_kb_sol_decode_coverage_observations_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_coverage_observations";
-}
-
-/// SQL statistics query for `kb_sol_mat_events`.
-pub(crate) fn table_stats_kb_sol_mat_events_sql() -> &'static str {
- return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_mat_events";
-}
-
-/// SQL creating `kb_sol_decode_events`.
-fn create_table_kb_sol_decode_events_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_events (
- id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL, input_hash TEXT NOT NULL, event_key TEXT NOT NULL,
- signature TEXT NOT NULL, slot BIGINT NOT NULL, instruction_path TEXT NOT NULL,
- program_id TEXT NOT NULL, protocol_code TEXT NOT NULL, surface_code TEXT NOT NULL,
- event_code TEXT NOT NULL, event_name TEXT NOT NULL, event_family TEXT NOT NULL,
- source_kind TEXT NOT NULL, confidence TEXT NOT NULL, proof_kind TEXT NOT NULL,
- proof_jsonb JSONB NOT NULL, payload_jsonb JSONB NOT NULL, transaction_failed BOOLEAN NOT NULL,
- transaction_error_jsonb JSONB, observation_committed BOOLEAN NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_events PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_decode_events_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed)
- )"#;
-}
-
-/// SQL creating the decoded observation stable identity index.
-fn create_ux_kb_sol_decode_events_identity_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_events_processor_input_event ON kb_sol_decode_events (processor_name, processor_version, input_key, event_key)";
-}
-
-/// SQL creating the decoded observation signature and path index.
-fn create_ix_kb_sol_decode_events_signature_path_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_signature_path ON kb_sol_decode_events (signature, instruction_path)";
-}
-
-/// SQL creating the decoded observation program and surface index.
-fn create_ix_kb_sol_decode_events_program_surface_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_program_surface ON kb_sol_decode_events (program_id, surface_code, event_code)";
-}
-
-/// SQL creating the decoded observation family and commit index.
-fn create_ix_kb_sol_decode_events_family_commit_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_family_commit ON kb_sol_decode_events (event_family, transaction_failed, observation_committed)";
-}
-
-/// SQL creating `kb_sol_decode_coverage_declarations`.
-fn create_table_kb_sol_decode_coverage_declarations_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_declarations (
- id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL,
- program_id TEXT NOT NULL, surface_code TEXT, entry_kind TEXT NOT NULL,
- entry_code TEXT NOT NULL, discriminator_hex TEXT, historical BOOLEAN NOT NULL DEFAULT FALSE,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_coverage_declarations PRIMARY KEY (id)
- )"#;
-}
-
-/// SQL creating the declared coverage stable identity index.
-fn create_ux_kb_sol_decode_coverage_declarations_identity_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_declarations_identity ON kb_sol_decode_coverage_declarations (processor_name, processor_version, program_id, COALESCE(surface_code, ''), entry_kind, entry_code, COALESCE(discriminator_hex, ''))";
-}
-
-/// SQL creating the declared coverage program index.
-fn create_ix_kb_sol_decode_coverage_declarations_program_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_declarations_program ON kb_sol_decode_coverage_declarations (program_id, processor_name, processor_version)";
-}
-
-/// SQL creating `kb_sol_decode_coverage_observations`.
-fn create_table_kb_sol_decode_coverage_observations_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_observations (
- id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL, input_hash TEXT NOT NULL, signature TEXT NOT NULL,
- slot BIGINT NOT NULL, instruction_path TEXT NOT NULL, program_id TEXT NOT NULL,
- surface_code TEXT, entry_code TEXT, discriminator_hex TEXT, status TEXT NOT NULL,
- recognized BOOLEAN NOT NULL, decoded_count INTEGER NOT NULL DEFAULT 0,
- materialized_count INTEGER NOT NULL DEFAULT 0, error_count INTEGER NOT NULL DEFAULT 0,
- transaction_failed BOOLEAN NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_decode_coverage_observations PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0),
- CONSTRAINT ck_kb_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0)
- )"#;
-}
-
-/// SQL creating the observed coverage stable identity index.
-fn create_ux_kb_sol_decode_coverage_observations_identity_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_observations_identity ON kb_sol_decode_coverage_observations (processor_name, processor_version, input_key)";
-}
-
-/// SQL creating the observed coverage program and status index.
-fn create_ix_kb_sol_decode_coverage_observations_program_status_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_program_status ON kb_sol_decode_coverage_observations (program_id, status, transaction_failed)";
-}
-
-/// SQL creating the observed coverage entry index.
-fn create_ix_kb_sol_decode_coverage_observations_entry_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_entry ON kb_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code)";
-}
-
-/// SQL creating `kb_sol_mat_events`.
-fn create_table_kb_sol_mat_events_sql() -> &'static str {
- return r#"CREATE TABLE IF NOT EXISTS kb_sol_mat_events (
- id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL,
- input_key TEXT NOT NULL, input_hash TEXT NOT NULL, output_key TEXT NOT NULL,
- source_event_key TEXT NOT NULL, source_decoder_name TEXT NOT NULL,
- source_decoder_version TEXT NOT NULL, source_decode_input_key TEXT NOT NULL,
- signature TEXT NOT NULL, slot BIGINT NOT NULL, materialized_family TEXT NOT NULL,
- payload_jsonb JSONB NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
- CONSTRAINT pk_kb_sol_mat_events PRIMARY KEY (id),
- CONSTRAINT ck_kb_sol_mat_events_slot_non_negative CHECK (slot >= 0)
- )"#;
-}
-
-/// SQL creating the materialized output stable identity index.
-fn create_ux_kb_sol_mat_events_identity_sql() -> &'static str {
- return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_mat_events_processor_input_output ON kb_sol_mat_events (processor_name, processor_version, input_key, output_key)";
-}
-
-/// SQL creating materialized output signature and family indexes.
-fn create_ix_kb_sol_mat_events_signature_family_sql() -> &'static str {
- return "CREATE INDEX IF NOT EXISTS ix_kb_sol_mat_events_signature_family ON kb_sol_mat_events (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family)";
+#[cfg(test)]
+fn statement_position(statements: &[&str], constraint_name: &str) -> usize {
+ for (index, statement) in statements.iter().enumerate() {
+ if statement.contains(constraint_name) {
+ return index;
+ }
+ }
+ return usize::MAX;
}
#[cfg(test)]
mod tests {
#[test]
- fn valid_table_name_accepts_domain_prefix() {
- assert!(super::is_valid_solana_table_name("kb_sol_raw_transactions"));
+ fn table_names_use_the_candidate_baseline_prefix() {
+ assert!(super::is_valid_solana_table_name("k_sol_raw_transactions"));
+ assert!(!super::is_valid_solana_table_name("kb_sol_raw_transactions"));
+ assert!(!super::is_valid_solana_table_name("k_sol_unknown_rows"));
}
#[test]
- fn table_name_rejects_explicit_schema() {
- let invalid_name = std::string::String::from("raw") + "." + "kb_sol_rpc_transactions";
- assert!(!super::is_valid_solana_table_name(invalid_name.as_str()));
- }
-
- #[test]
- fn table_name_rejects_unknown_domain() {
- assert!(!super::is_valid_solana_table_name("kb_sol_unknown_rows"));
- }
-
- #[test]
- fn table_name_rejects_missing_table_suffix() {
- assert!(!super::is_valid_solana_table_name("kb_sol_raw"));
- }
-
- #[test]
- fn table_name_rejects_uppercase() {
- assert!(!super::is_valid_solana_table_name("kb_sol_raw_RPC_transactions"));
- }
-
- #[test]
- fn raw_store_table_names_are_valid() {
- let result = crate::validate_raw_store_table_names();
+ fn embedded_sql_resources_are_atomic() {
+ let result = crate::validate_postgres_sql_resources();
assert!(result.is_ok());
}
#[test]
- fn core_store_table_names_are_valid() {
- let result = crate::validate_core_store_table_names();
- assert!(result.is_ok());
+ fn candidate_baseline_inventory_has_sixteen_tables_and_seventy_five_indexes() {
+ let table_count = super::RAW_STORE_TABLE_NAMES.len()
+ + super::CORE_STORE_TABLE_NAMES.len()
+ + super::DECODE_STORE_TABLE_NAMES.len();
+ assert_eq!(table_count, 16);
+ assert_eq!(crate::expected_postgres_index_names().len(), 75);
}
#[test]
- fn decode_store_table_names_are_valid() {
- let result = crate::validate_decode_store_table_names();
- assert!(result.is_ok());
+ fn embedded_resource_inventory_is_complete_and_uses_no_legacy_schema_name() {
+ let schema_groups = [
+ crate::raw_store_schema_statements(),
+ crate::core_store_schema_statements(),
+ crate::decode_store_schema_statements(),
+ ];
+ let mut schema_count = 0_usize;
+ for group in schema_groups {
+ schema_count += group.len();
+ for statement in group {
+ assert!(!statement.contains("kb_sol_"));
+ }
+ }
+ let maintenance_count = super::truncate_store_schema_statements().len()
+ + super::drop_store_schema_statements().len();
+ assert_eq!(schema_count + maintenance_count, 236);
}
#[test]
- fn decode_store_schema_contains_no_create_schema_statement() {
- let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA";
- for statement in crate::decode_store_schema_statements() {
- assert!(!statement.contains(forbidden.as_str()));
+ fn foreign_keys_are_scheduled_after_referenced_primary_keys() {
+ let mut statements = crate::raw_store_schema_statements();
+ statements.extend(crate::core_store_schema_statements());
+ statements.extend(crate::decode_store_schema_statements());
+ let dependencies = [
+ (
+ "pk_k_sol_raw_transactions",
+ "fk_k_sol_obs_transaction_observations_raw_transaction",
+ ),
+ ("pk_k_sol_raw_transactions", "fk_k_sol_core_transactions_raw_transaction"),
+ ("pk_k_sol_obs_account_observations", "fk_k_sol_core_account_states_observation"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_account_keys_transaction"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_balance_changes_transaction"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_inner_instructions_transaction"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_instructions_transaction"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_logs_transaction"),
+ ("pk_k_sol_core_transactions", "fk_k_sol_core_return_data_transaction"),
+ ];
+ for (required_constraint, dependent_constraint) in dependencies {
+ let required_position = super::statement_position(&statements, required_constraint);
+ let dependent_position = super::statement_position(&statements, dependent_constraint);
+ assert!(required_position < dependent_position);
}
}
#[test]
- fn raw_store_schema_contains_no_create_schema_statement() {
- let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA";
- for statement in crate::raw_store_schema_statements() {
- assert!(!statement.contains(forbidden.as_str()));
- }
- }
-
- #[test]
- fn core_store_schema_contains_no_create_schema_statement() {
- let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA";
- for statement in crate::core_store_schema_statements() {
- assert!(!statement.contains(forbidden.as_str()));
- }
- }
-
- #[test]
- fn raw_store_schema_uses_prefixed_index_names() {
- let statements = crate::raw_store_schema_statements();
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("ux_kb_sol_raw_transactions_signature");
- }));
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("ix_kb_sol_obs_transaction_observations_signature");
- }));
- }
-
- #[test]
- fn core_store_schema_uses_prefixed_constraint_and_index_names() {
- let statements = crate::core_store_schema_statements();
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("pk_kb_sol_core_transactions");
- }));
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("fk_kb_sol_core_instructions_transaction");
- }));
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("ux_kb_sol_core_instructions_sig_path");
- }));
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("ix_kb_sol_core_instructions_program");
- }));
- assert!(statements.iter().any(|statement| -> bool {
- return statement.contains("ux_kb_sol_ops_processing_ledger_identity");
- }));
+ fn all_store_table_names_are_valid() {
+ assert!(super::validate_raw_store_table_names().is_ok());
+ assert!(super::validate_core_store_table_names().is_ok());
+ assert!(super::validate_decode_store_table_names().is_ok());
}
}
diff --git a/ks-store/src/postgres/query.rs b/ks-store/src/postgres/query.rs
index 3c1e37f..a619b56 100644
--- a/ks-store/src/postgres/query.rs
+++ b/ks-store/src/postgres/query.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/postgres/query.rs
-// version: 4
+// version: 5
//! PostgreSQL query modules.
@@ -9,13 +9,13 @@ mod decode_pipeline_queries;
mod health_queries;
mod raw_queries;
mod replay_candidate_queries;
+mod schema_queries;
mod table_diagnostics_queries;
pub(crate) use self::core_extraction_queries::is_core_extraction_current;
pub(crate) use self::core_extraction_queries::list_raw_transactions_for_core_extraction;
pub(crate) use self::core_extraction_queries::mark_core_extraction_failed;
pub(crate) use self::core_extraction_queries::persist_core_extraction;
-pub(crate) use self::core_queries::apply_core_store_schema;
pub(crate) use self::core_queries::insert_core_account_keys;
pub(crate) use self::core_queries::insert_core_balance_changes;
pub(crate) use self::core_queries::insert_core_inner_instructions;
@@ -26,21 +26,17 @@ pub(crate) use self::core_queries::list_core_instruction_replay_inputs;
pub(crate) use self::core_queries::list_core_instructions_for_replay;
pub(crate) use self::core_queries::list_decode_replay_inputs;
pub(crate) use self::core_queries::update_core_instruction_lifecycle;
-pub(crate) use self::decode_pipeline_queries::apply_decode_store_schema;
pub(crate) use self::decode_pipeline_queries::is_decode_current;
pub(crate) use self::decode_pipeline_queries::list_decode_coverage_summary;
pub(crate) use self::decode_pipeline_queries::list_decode_inputs;
-pub(crate) use self::decode_pipeline_queries::list_materialized_events;
+pub(crate) use self::decode_pipeline_queries::list_materialized_outputs;
pub(crate) use self::decode_pipeline_queries::mark_decode_failed;
pub(crate) use self::decode_pipeline_queries::persist_decode_coverage_declarations;
pub(crate) use self::decode_pipeline_queries::persist_decode_result;
pub(crate) use self::decode_pipeline_queries::persist_materialization_result;
pub(crate) use self::health_queries::load_current_schema;
-pub(crate) use self::health_queries::load_latest_migration_version;
-pub(crate) use self::health_queries::load_migration_table_name;
pub(crate) use self::health_queries::load_server_version;
pub(crate) use self::health_queries::run_health_check;
-pub(crate) use self::raw_queries::apply_raw_store_schema;
pub(crate) use self::raw_queries::has_raw_transaction_signature;
pub(crate) use self::raw_queries::has_transaction_observation_key;
pub(crate) use self::raw_queries::insert_raw_transaction;
@@ -49,5 +45,7 @@ pub(crate) use self::raw_queries::update_raw_payload_lifecycle;
pub(crate) use self::replay_candidate_queries::list_replay_entity_summaries;
pub(crate) use self::replay_candidate_queries::list_replay_program_summaries;
pub(crate) use self::replay_candidate_queries::list_replay_transaction_candidates;
+pub(crate) use self::schema_queries::apply_store_schema;
+pub(crate) use self::schema_queries::load_expected_index_counts;
pub(crate) use self::table_diagnostics_queries::load_table_statistics;
pub(crate) use self::table_diagnostics_queries::table_exists;
diff --git a/ks-store/src/postgres/query/core_extraction_queries.rs b/ks-store/src/postgres/query/core_extraction_queries.rs
index 634f8e9..e38c6da 100644
--- a/ks-store/src/postgres/query/core_extraction_queries.rs
+++ b/ks-store/src/postgres/query/core_extraction_queries.rs
@@ -1,5 +1,5 @@
// file: ks-store/src/postgres/query/core_extraction_queries.rs
-// version: 6
+// version: 8
//! PostgreSQL queries for atomic canonical transaction to core extraction.
@@ -9,25 +9,21 @@ pub(crate) async fn list_raw_transactions_for_core_extraction(
pool: &sqlx::PgPool,
filter: &crate::CoreExtractionSelectionFilter,
) -> ks_core::Result> {
- let min_slot_result =
- crate::postgres::query::core_extraction_queries::optional_sql_slot(filter.min_slot);
+ let min_slot_result = optional_sql_slot(filter.min_slot);
let min_slot = match min_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let max_slot_result =
- crate::postgres::query::core_extraction_queries::optional_sql_slot(filter.max_slot);
+ let max_slot_result = optional_sql_slot(filter.max_slot);
let max_slot = match max_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let processing_state = filter
- .processing_state
- .map(crate::postgres::query::core_extraction_queries::raw_processing_state_to_sql);
+ let processing_state = filter.processing_state.map(raw_processing_state_to_sql);
let limit = i64::from(filter.limit);
let signatures_are_empty = filter.signatures.is_empty();
let query_result = sqlx::query(
- "SELECT raw.id, raw.signature, raw.slot, raw.canonical_json, raw.canonical_json_hash, raw.canonical_format_version, raw.retention_state, raw.processing_state, raw.created_at, raw.updated_at FROM kb_sol_raw_transactions raw WHERE ($1 OR raw.signature = ANY($2)) AND ($3::BIGINT IS NULL OR raw.slot >= $3) AND ($4::BIGINT IS NULL OR raw.slot <= $4) AND ($5::TEXT IS NULL OR raw.processing_state = $5) AND ($6::TEXT IS NULL OR EXISTS (SELECT 1 FROM kb_sol_core_instructions instruction WHERE instruction.signature = raw.signature AND instruction.program_id = $6)) ORDER BY raw.slot ASC, raw.signature ASC LIMIT $7",
+ "SELECT raw.id, raw.signature, raw.slot, raw.block_time, raw.canonical_json, raw.canonical_json_hash, raw.canonical_format_version, raw.retention_state, raw.processing_state, raw.created_at, raw.updated_at FROM k_sol_raw_transactions raw WHERE ($1 OR raw.signature = ANY($2)) AND ($3::BIGINT IS NULL OR raw.slot >= $3) AND ($4::BIGINT IS NULL OR raw.slot <= $4) AND ($5::TEXT IS NULL OR raw.processing_state = $5) AND ($6::TEXT IS NULL OR EXISTS (SELECT 1 FROM k_sol_core_instructions instruction WHERE instruction.signature = raw.signature AND instruction.program_id = $6)) ORDER BY raw.slot ASC, raw.signature ASC LIMIT $7",
)
.bind(signatures_are_empty)
.bind(&filter.signatures)
@@ -48,8 +44,7 @@ pub(crate) async fn list_raw_transactions_for_core_extraction(
};
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
- let mapped_result =
- crate::postgres::query::core_extraction_queries::map_raw_transaction_row(&row);
+ let mapped_result = map_raw_transaction_row(&row);
let mapped = match mapped_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -64,7 +59,7 @@ pub(crate) async fn is_core_extraction_current(
identity: &crate::ProcessingLedgerIdentity,
) -> ks_core::Result {
let query_result = sqlx::query_scalar::(
- "SELECT EXISTS(SELECT 1 FROM kb_sol_ops_processing_ledger WHERE stage = $1 AND processor_name = $2 AND processor_version = $3 AND input_key = $4 AND input_hash = $5 AND status = 'succeeded')",
+ "SELECT EXISTS(SELECT 1 FROM k_sol_ops_processing_ledger WHERE stage = $1 AND processor_name = $2 AND processor_version = $3 AND input_key = $4 AND input_hash = $5 AND status = 'succeeded')",
)
.bind(identity.stage.as_str())
.bind(identity.processor_name.as_str())
@@ -100,7 +95,7 @@ pub(crate) async fn persist_core_extraction(
},
};
let existing_result = sqlx::query_scalar::(
- "SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1",
+ "SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1",
)
.bind(bundle.transaction.signature.as_str())
.fetch_optional(&mut *transaction)
@@ -114,11 +109,10 @@ pub(crate) async fn persist_core_extraction(
},
};
if existing.is_some() {
- let delete_result =
- sqlx::query("DELETE FROM kb_sol_core_transactions WHERE signature = $1")
- .bind(bundle.transaction.signature.as_str())
- .execute(&mut *transaction)
- .await;
+ let delete_result = sqlx::query("DELETE FROM k_sol_core_transactions WHERE signature = $1")
+ .bind(bundle.transaction.signature.as_str())
+ .execute(&mut *transaction)
+ .await;
if let std::result::Result::Err(error) = delete_result {
return std::result::Result::Err(ks_core::Error::db(format!(
"postgres core extraction replacement delete failed: {error}"
@@ -132,56 +126,51 @@ pub(crate) async fn persist_core_extraction(
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let account_result =
- crate::postgres::query::core_extraction_queries::insert_account_keys_in_transaction(
- &mut transaction,
- transaction_id,
- &bundle.account_keys,
- )
- .await;
+ insert_account_keys_in_transaction(&mut transaction, transaction_id, &bundle.account_keys)
+ .await;
if let std::result::Result::Err(error) = account_result {
return std::result::Result::Err(error);
}
let instruction_result =
- crate::postgres::query::core_extraction_queries::insert_instructions_in_transaction(
- &mut transaction,
- transaction_id,
- &bundle.instructions,
- )
- .await;
+ insert_instructions_in_transaction(&mut transaction, transaction_id, &bundle.instructions)
+ .await;
if let std::result::Result::Err(error) = instruction_result {
return std::result::Result::Err(error);
}
- let inner_result =
- crate::postgres::query::core_extraction_queries::insert_inner_instructions_in_transaction(
- &mut transaction,
- transaction_id,
- &bundle.inner_instructions,
- )
- .await;
+ let inner_result = insert_inner_instructions_in_transaction(
+ &mut transaction,
+ transaction_id,
+ &bundle.inner_instructions,
+ )
+ .await;
if let std::result::Result::Err(error) = inner_result {
return std::result::Result::Err(error);
}
- let log_result = crate::postgres::query::core_extraction_queries::insert_logs_in_transaction(
- &mut transaction,
- transaction_id,
- &bundle.logs,
- )
- .await;
+ let log_result =
+ insert_logs_in_transaction(&mut transaction, transaction_id, &bundle.logs).await;
if let std::result::Result::Err(error) = log_result {
return std::result::Result::Err(error);
}
- let balance_result =
- crate::postgres::query::core_extraction_queries::insert_balance_changes_in_transaction(
- &mut transaction,
- transaction_id,
- &bundle.balance_changes,
- )
- .await;
+ let balance_result = insert_balance_changes_in_transaction(
+ &mut transaction,
+ transaction_id,
+ &bundle.balance_changes,
+ )
+ .await;
if let std::result::Result::Err(error) = balance_result {
return std::result::Result::Err(error);
}
+ let return_data_result = insert_return_data_in_transaction(
+ &mut transaction,
+ transaction_id,
+ bundle.return_data.as_ref(),
+ )
+ .await;
+ if let std::result::Result::Err(error) = return_data_result {
+ return std::result::Result::Err(error);
+ }
let raw_update_result = sqlx::query(
- "UPDATE kb_sol_raw_transactions SET processing_state = 'core_extracted', lifecycle_reason = NULL, updated_at = NOW() WHERE id = $1",
+ "UPDATE k_sol_raw_transactions SET processing_state = 'core_extracted', lifecycle_reason = NULL, updated_at = NOW() WHERE id = $1",
)
.bind(bundle.raw_transaction_id)
.execute(&mut *transaction)
@@ -191,15 +180,14 @@ pub(crate) async fn persist_core_extraction(
"postgres core extraction raw lifecycle update failed: {error}"
)));
}
- let ledger_result =
- crate::postgres::query::core_extraction_queries::upsert_ledger_terminal_in_transaction(
- &mut transaction,
- &bundle.ledger_identity,
- "succeeded",
- std::option::Option::None,
- std::option::Option::None,
- )
- .await;
+ let ledger_result = upsert_ledger_terminal_in_transaction(
+ &mut transaction,
+ &bundle.ledger_identity,
+ "succeeded",
+ std::option::Option::None,
+ std::option::Option::None,
+ )
+ .await;
if let std::result::Result::Err(error) = ledger_result {
return std::result::Result::Err(error);
}
@@ -209,8 +197,7 @@ pub(crate) async fn persist_core_extraction(
"postgres core extraction commit failed: {error}"
)));
}
- let inserted_count_result =
- crate::postgres::query::core_extraction_queries::bundle_inserted_count(bundle);
+ let inserted_count_result = bundle_inserted_count(bundle);
let inserted_count = match inserted_count_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -236,7 +223,7 @@ pub(crate) async fn mark_core_extraction_failed(
},
};
let raw_update_result = sqlx::query(
- "UPDATE kb_sol_raw_transactions SET processing_state = 'failed', lifecycle_reason = $2, updated_at = NOW() WHERE id = $1",
+ "UPDATE k_sol_raw_transactions SET processing_state = 'failed', lifecycle_reason = $2, updated_at = NOW() WHERE id = $1",
)
.bind(failure.raw_transaction_id)
.bind(failure.error_message.as_str())
@@ -247,15 +234,14 @@ pub(crate) async fn mark_core_extraction_failed(
"postgres core extraction failed lifecycle update failed: {error}"
)));
}
- let ledger_result =
- crate::postgres::query::core_extraction_queries::upsert_ledger_terminal_in_transaction(
- &mut transaction,
- &failure.ledger_identity,
- "failed",
- std::option::Option::Some(failure.error_code.as_str()),
- std::option::Option::Some(failure.error_message.as_str()),
- )
- .await;
+ let ledger_result = upsert_ledger_terminal_in_transaction(
+ &mut transaction,
+ &failure.ledger_identity,
+ "failed",
+ std::option::Option::Some(failure.error_code.as_str()),
+ std::option::Option::Some(failure.error_message.as_str()),
+ )
+ .await;
if let std::result::Result::Err(error) = ledger_result {
return std::result::Result::Err(error);
}
@@ -272,17 +258,18 @@ async fn insert_core_transaction_in_transaction(
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
input: &crate::CoreTransactionInsert,
) -> ks_core::Result {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_transactions (raw_transaction_id, signature, slot, failed, err_json) VALUES ($1, $2, $3, $4, $5) RETURNING id",
+ "INSERT INTO k_sol_core_transactions (raw_transaction_id, signature, slot, block_time, failed, err_json) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id",
)
.bind(input.raw_transaction_id)
.bind(input.signature.as_str())
.bind(slot)
+ .bind(input.block_time)
.bind(input.failed)
.bind(input.err_json.clone())
.fetch_one(&mut **transaction)
@@ -301,7 +288,7 @@ async fn insert_account_keys_in_transaction(
inputs: &[crate::CoreAccountKeyInsert],
) -> ks_core::Result<()> {
for input in inputs {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -316,14 +303,14 @@ async fn insert_account_keys_in_transaction(
},
};
let query_result = sqlx::query(
- "INSERT INTO kb_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
+ "INSERT INTO k_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
)
.bind(transaction_id)
.bind(input.signature.as_str())
.bind(slot)
.bind(account_index)
.bind(input.account_key.as_str())
- .bind(crate::postgres::query::core_extraction_queries::account_key_source_to_sql(input.source))
+ .bind(account_key_source_to_sql(input.source))
.bind(input.writable)
.bind(input.signer)
.bind(input.executable)
@@ -344,19 +331,25 @@ async fn insert_instructions_in_transaction(
inputs: &[crate::CoreInstructionInsert],
) -> ks_core::Result<()> {
for input in inputs {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
+ let stack_height_result = sql_stack_height(input.stack_height, "core instruction");
+ let stack_height = match stack_height_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
let query_result = sqlx::query(
- "INSERT INTO kb_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'pending')",
+ "INSERT INTO k_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'pending')",
)
.bind(transaction_id)
.bind(input.signature.as_str())
.bind(slot)
.bind(input.instruction_path.as_str())
.bind(input.program_id.as_str())
+ .bind(stack_height)
.bind(&input.accounts_json)
.bind(&input.payload_json)
.bind(input.payload_json_hash.as_deref())
@@ -377,13 +370,18 @@ async fn insert_inner_instructions_in_transaction(
inputs: &[crate::CoreInnerInstructionInsert],
) -> ks_core::Result<()> {
for input in inputs {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
+ let stack_height_result = sql_stack_height(input.stack_height, "core inner instruction");
+ let stack_height = match stack_height_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
let query_result = sqlx::query(
- "INSERT INTO kb_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, accounts_json, payload_json, payload_json_hash) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
+ "INSERT INTO k_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'pending')",
)
.bind(transaction_id)
.bind(input.signature.as_str())
@@ -391,6 +389,7 @@ async fn insert_inner_instructions_in_transaction(
.bind(input.parent_instruction_path.as_str())
.bind(input.instruction_path.as_str())
.bind(input.program_id.as_str())
+ .bind(stack_height)
.bind(&input.accounts_json)
.bind(&input.payload_json)
.bind(input.payload_json_hash.as_deref())
@@ -405,13 +404,45 @@ async fn insert_inner_instructions_in_transaction(
return std::result::Result::Ok(());
}
+async fn insert_return_data_in_transaction(
+ transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
+ transaction_id: i64,
+ input: std::option::Option<&crate::CoreReturnDataInsert>,
+) -> ks_core::Result<()> {
+ let value = match input {
+ std::option::Option::Some(value) => value,
+ std::option::Option::None => return std::result::Result::Ok(()),
+ };
+ let slot_result = sql_slot(value.slot);
+ let slot = match slot_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
+ let query_result = sqlx::query(
+ "INSERT INTO k_sol_core_return_data (transaction_id, signature, slot, program_id, data_base64) VALUES ($1, $2, $3, $4, $5)",
+ )
+ .bind(transaction_id)
+ .bind(value.signature.as_str())
+ .bind(slot)
+ .bind(value.program_id.as_str())
+ .bind(value.data_base64.as_str())
+ .execute(&mut **transaction)
+ .await;
+ return match query_result {
+ std::result::Result::Ok(_) => std::result::Result::Ok(()),
+ std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::db(format!(
+ "postgres core extraction return-data insert failed: {error}"
+ ))),
+ };
+}
+
async fn insert_logs_in_transaction(
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
transaction_id: i64,
inputs: &[crate::CoreLogInsert],
) -> ks_core::Result<()> {
for input in inputs {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -426,7 +457,7 @@ async fn insert_logs_in_transaction(
},
};
let query_result = sqlx::query(
- "INSERT INTO kb_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
+ "INSERT INTO k_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
)
.bind(transaction_id)
.bind(input.signature.as_str())
@@ -453,7 +484,7 @@ async fn insert_balance_changes_in_transaction(
inputs: &[crate::CoreBalanceChangeInsert],
) -> ks_core::Result<()> {
for input in inputs {
- let slot_result = crate::postgres::query::core_extraction_queries::sql_slot(input.slot);
+ let slot_result = sql_slot(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -477,13 +508,13 @@ async fn insert_balance_changes_in_transaction(
},
};
let query_result = sqlx::query(
- "INSERT INTO kb_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
+ "INSERT INTO k_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
)
.bind(transaction_id)
.bind(input.signature.as_str())
.bind(slot)
.bind(balance_index)
- .bind(crate::postgres::query::core_extraction_queries::balance_change_kind_to_sql(input.balance_kind))
+ .bind(balance_change_kind_to_sql(input.balance_kind))
.bind(account_index)
.bind(input.account_key.as_deref())
.bind(input.mint.as_deref())
@@ -510,7 +541,7 @@ async fn upsert_ledger_terminal_in_transaction(
error_message: std::option::Option<&str>,
) -> ks_core::Result<()> {
let query_result = sqlx::query(
- "INSERT INTO kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key, input_hash, status, attempt_count, started_at, finished_at, error_code, error_message) VALUES ($1, $2, $3, $4, $5, $6, 1, NOW(), NOW(), $7, $8) ON CONFLICT (stage, processor_name, processor_version, input_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, status = EXCLUDED.status, attempt_count = kb_sol_ops_processing_ledger.attempt_count + 1, started_at = NOW(), finished_at = NOW(), error_code = EXCLUDED.error_code, error_message = EXCLUDED.error_message, updated_at = NOW()",
+ "INSERT INTO k_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key, input_hash, status, attempt_count, started_at, finished_at, error_code, error_message) VALUES ($1, $2, $3, $4, $5, $6, 1, NOW(), NOW(), $7, $8) ON CONFLICT (stage, processor_name, processor_version, input_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, status = EXCLUDED.status, attempt_count = k_sol_ops_processing_ledger.attempt_count + 1, started_at = NOW(), finished_at = NOW(), error_code = EXCLUDED.error_code, error_message = EXCLUDED.error_message, updated_at = NOW()",
)
.bind(identity.stage.as_str())
.bind(identity.processor_name.as_str())
@@ -551,18 +582,12 @@ fn map_raw_transaction_row(
)));
},
};
- let retention_result =
- crate::postgres::query::core_extraction_queries::raw_retention_state_from_sql(
- retention_text.as_str(),
- );
+ let retention_result = raw_retention_state_from_sql(retention_text.as_str());
let retention_state = match retention_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let processing_result =
- crate::postgres::query::core_extraction_queries::raw_processing_state_from_sql(
- processing_text.as_str(),
- );
+ let processing_result = raw_processing_state_from_sql(processing_text.as_str());
let processing_state = match processing_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -591,6 +616,14 @@ fn map_raw_transaction_row(
)));
},
};
+ let block_time = match row.try_get::, _>("block_time") {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => {
+ return std::result::Result::Err(ks_core::Error::db(format!(
+ "postgres raw block time mapping failed: {error}"
+ )));
+ },
+ };
let canonical_json =
match row.try_get::, _>("canonical_json") {
std::result::Result::Ok(value) => value,
@@ -637,6 +670,7 @@ fn map_raw_transaction_row(
id,
signature,
slot,
+ block_time,
canonical_json,
canonical_json_hash,
canonical_format_version,
@@ -655,6 +689,7 @@ fn bundle_inserted_count(bundle: &crate::CoreExtractionBundle) -> ks_core::Resul
bundle.inner_instructions.len(),
bundle.logs.len(),
bundle.balance_changes.len(),
+ usize::from(bundle.return_data.is_some()),
] {
let value_result = u64::try_from(length);
let value = match value_result {
@@ -678,10 +713,23 @@ fn bundle_inserted_count(bundle: &crate::CoreExtractionBundle) -> ks_core::Resul
return std::result::Result::Ok(total);
}
+fn sql_stack_height(
+ value: std::option::Option,
+ context: &str,
+) -> ks_core::Result> {
+ let conversion_result = value.map(i32::try_from).transpose();
+ return match conversion_result {
+ std::result::Result::Ok(converted) => std::result::Result::Ok(converted),
+ std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::db(format!(
+ "{context} stack height does not fit into SQL INTEGER: {error}"
+ ))),
+ };
+}
+
fn optional_sql_slot(value: std::option::Option) -> ks_core::Result> {
return match value {
std::option::Option::Some(slot) => {
- let converted_result = crate::postgres::query::core_extraction_queries::sql_slot(slot);
+ let converted_result = sql_slot(slot);
match converted_result {
std::result::Result::Ok(converted) => {
std::result::Result::Ok(std::option::Option::Some(converted))
@@ -760,16 +808,14 @@ mod tests {
#[test]
fn processing_state_serializes_to_expected_sql() {
assert_eq!(
- crate::postgres::query::core_extraction_queries::raw_processing_state_to_sql(
- crate::RawPayloadProcessingState::CoreExtracted,
- ),
+ super::raw_processing_state_to_sql(crate::RawPayloadProcessingState::CoreExtracted,),
"core_extracted"
);
}
#[test]
fn slot_rejects_values_above_bigint() {
- let result = crate::postgres::query::core_extraction_queries::sql_slot(u64::MAX);
+ let result = super::sql_slot(u64::MAX);
assert!(result.is_err());
}
@@ -790,7 +836,7 @@ mod tests {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("unexpected connect error: {error}"),
};
- let schema_result = store.initialize_core_store_schema().await;
+ let schema_result = store.initialize_store_schema().await;
if let std::result::Result::Err(error) = schema_result {
panic!("unexpected schema error: {error}");
}
@@ -798,6 +844,7 @@ mod tests {
let raw_input_result = crate::RawTransactionInsert::new(
signature.clone(),
1,
+ std::option::Option::None,
serde_json::json!({"test": "core_extraction_rollback"}),
1,
);
@@ -816,7 +863,7 @@ mod tests {
panic!("unexpected raw insert error: {error}");
}
let raw_id_result = sqlx::query_scalar::(
- "SELECT id FROM kb_sol_raw_transactions WHERE signature = $1",
+ "SELECT id FROM k_sol_raw_transactions WHERE signature = $1",
)
.bind(signature.as_str())
.fetch_one(store.pool())
@@ -855,8 +902,13 @@ mod tests {
signature: &str,
duplicate_account_index: bool,
) -> crate::CoreExtractionBundle {
- let transaction_result =
- crate::CoreTransactionInsert::new(signature, 1, false, std::option::Option::None);
+ let transaction_result = crate::CoreTransactionInsert::new(
+ signature,
+ 1,
+ std::option::Option::None,
+ false,
+ std::option::Option::None,
+ );
let transaction = match transaction_result {
std::result::Result::Ok(value) => value.with_raw_transaction_id(raw_transaction_id),
std::result::Result::Err(error) => panic!("unexpected transaction error: {error}"),
@@ -915,6 +967,7 @@ mod tests {
inner_instructions: std::vec::Vec::new(),
logs: std::vec::Vec::new(),
balance_changes: std::vec::Vec::new(),
+ return_data: std::option::Option::None,
};
}
@@ -926,7 +979,7 @@ mod tests {
expected_ledger_count: i64,
) {
let core_count_result = sqlx::query_scalar::(
- "SELECT COUNT(*)::BIGINT FROM kb_sol_core_transactions WHERE signature = $1",
+ "SELECT COUNT(*)::BIGINT FROM k_sol_core_transactions WHERE signature = $1",
)
.bind(signature)
.fetch_one(store.pool())
@@ -936,7 +989,7 @@ mod tests {
std::result::Result::Err(error) => panic!("unexpected core count error: {error}"),
};
let account_count_result = sqlx::query_scalar::(
- "SELECT COUNT(*)::BIGINT FROM kb_sol_core_account_keys WHERE signature = $1",
+ "SELECT COUNT(*)::BIGINT FROM k_sol_core_account_keys WHERE signature = $1",
)
.bind(signature)
.fetch_one(store.pool())
@@ -946,7 +999,7 @@ mod tests {
std::result::Result::Err(error) => panic!("unexpected account count error: {error}"),
};
let processing_state_result = sqlx::query_scalar::(
- "SELECT processing_state FROM kb_sol_raw_transactions WHERE signature = $1",
+ "SELECT processing_state FROM k_sol_raw_transactions WHERE signature = $1",
)
.bind(signature)
.fetch_one(store.pool())
@@ -956,7 +1009,7 @@ mod tests {
std::result::Result::Err(error) => panic!("unexpected processing state error: {error}"),
};
let ledger_count_result = sqlx::query_scalar::(
- "SELECT COUNT(*)::BIGINT FROM kb_sol_ops_processing_ledger WHERE stage = 'core_extraction' AND processor_name = 'canonical_to_core' AND processor_version = 'rollback-test' AND input_key = $1",
+ "SELECT COUNT(*)::BIGINT FROM k_sol_ops_processing_ledger WHERE stage = 'core_extraction' AND processor_name = 'canonical_to_core' AND processor_version = 'rollback-test' AND input_key = $1",
)
.bind(signature)
.fetch_one(store.pool())
@@ -973,7 +1026,7 @@ mod tests {
async fn cleanup_rollback_test(store: &crate::PostgresStore, signature: &str) {
let ledger_result = sqlx::query(
- "DELETE FROM kb_sol_ops_processing_ledger WHERE processor_version = 'rollback-test' AND input_key = $1",
+ "DELETE FROM k_sol_ops_processing_ledger WHERE processor_version = 'rollback-test' AND input_key = $1",
)
.bind(signature)
.execute(store.pool())
@@ -981,14 +1034,14 @@ mod tests {
if let std::result::Result::Err(error) = ledger_result {
panic!("unexpected ledger cleanup error: {error}");
}
- let core_result = sqlx::query("DELETE FROM kb_sol_core_transactions WHERE signature = $1")
+ let core_result = sqlx::query("DELETE FROM k_sol_core_transactions WHERE signature = $1")
.bind(signature)
.execute(store.pool())
.await;
if let std::result::Result::Err(error) = core_result {
panic!("unexpected core cleanup error: {error}");
}
- let raw_result = sqlx::query("DELETE FROM kb_sol_raw_transactions WHERE signature = $1")
+ let raw_result = sqlx::query("DELETE FROM k_sol_raw_transactions WHERE signature = $1")
.bind(signature)
.execute(store.pool())
.await;
diff --git a/ks-store/src/postgres/query/core_queries.rs b/ks-store/src/postgres/query/core_queries.rs
index 99a80f0..de8e99f 100644
--- a/ks-store/src/postgres/query/core_queries.rs
+++ b/ks-store/src/postgres/query/core_queries.rs
@@ -1,75 +1,34 @@
// file: ks-store/src/postgres/query/core_queries.rs
-// version: 6
+// version: 9
//! PostgreSQL queries for normalized Solana core storage.
use sqlx::Row; // rust-rules: trait-import
-const OUTER_INSTRUCTIONS_CONTEXT_SQL: &str = "SELECT COALESCE(jsonb_agg(jsonb_build_object('instructionIndex', instruction_path::BIGINT, 'instructionPath', instruction_path, 'programId', program_id, 'payloadJson', payload_json, 'payloadHash', payload_json_hash) ORDER BY instruction_path::BIGINT, instruction_path ASC), '[]'::jsonb) FROM kb_sol_core_instructions WHERE signature = $1 AND instruction_path ~ '^[0-9]+$'";
-
-pub(crate) async fn apply_core_store_schema(pool: &sqlx::PgPool) -> ks_core::Result<()> {
- let validation_result = crate::validate_core_store_table_names();
- if let std::result::Result::Err(error) = validation_result {
- return std::result::Result::Err(error);
- }
- let transaction_result = pool.begin().await;
- let mut transaction = match transaction_result {
- std::result::Result::Ok(value) => value,
- std::result::Result::Err(error) => {
- return std::result::Result::Err(ks_core::Error::db(format!(
- "postgres core store schema transaction failed: {error}"
- )));
- },
- };
- let lock_result = sqlx::query("SELECT pg_advisory_xact_lock($1)")
- .bind(crate::STORE_SCHEMA_ADVISORY_LOCK_ID)
- .execute(&mut *transaction)
- .await;
- if let std::result::Result::Err(error) = lock_result {
- return std::result::Result::Err(ks_core::Error::db(format!(
- "postgres core store schema advisory lock failed: {error}"
- )));
- }
- for statement in crate::core_store_schema_statements() {
- let execution_result = sqlx::query(statement).execute(&mut *transaction).await;
- if let std::result::Result::Err(error) = execution_result {
- return std::result::Result::Err(ks_core::Error::db(format!(
- "postgres core store schema initialization failed: {error}"
- )));
- }
- }
- let commit_result = transaction.commit().await;
- if let std::result::Result::Err(error) = commit_result {
- return std::result::Result::Err(ks_core::Error::db(format!(
- "postgres core store schema commit failed: {error}"
- )));
- }
- return std::result::Result::Ok(());
-}
+const TOP_LEVEL_INSTRUCTIONS_CONTEXT_SQL: &str = "SELECT COALESCE(jsonb_agg(jsonb_build_object('instructionIndex', instruction_path::BIGINT, 'instructionPath', instruction_path, 'programId', program_id, 'stackHeight', stack_height, 'payloadJson', payload_json, 'payloadHash', payload_json_hash) ORDER BY instruction_path::BIGINT, instruction_path ASC), '[]'::jsonb) FROM k_sol_core_instructions WHERE signature = $1 AND instruction_path ~ '^[0-9]+$'";
+const INCOMPLETE_DECODE_INPUTS_SQL: &str = "WITH incomplete AS (SELECT signature, MIN(slot) AS first_slot FROM k_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (processing_state = ANY($2) OR (processing_state = 'ignored' AND lifecycle_reason = 'unsupported')) GROUP BY signature ORDER BY MIN(slot), signature LIMIT $7) SELECT i.id, i.transaction_id, i.signature, i.slot, i.instruction_path, i.program_id, i.stack_height, i.accounts_json, i.payload_json, i.payload_json_hash, i.processing_state, i.created_at, i.updated_at FROM k_sol_core_instructions i JOIN incomplete s ON s.signature = i.signature WHERE (cardinality($5::text[]) = 0 OR i.program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR i.instruction_path = ANY($6)) ORDER BY i.slot ASC, i.signature ASC, i.instruction_path ASC";
pub(crate) async fn insert_core_transaction(
pool: &sqlx::PgPool,
input: &crate::CoreTransactionInsert,
) -> ks_core::Result {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_transactions (raw_transaction_id, signature, slot, failed, err_json) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (signature) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_transactions (raw_transaction_id, signature, slot, block_time, failed, err_json) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (signature) DO NOTHING RETURNING id",
)
.bind(input.raw_transaction_id)
.bind(input.signature.as_str())
.bind(slot)
+ .bind(input.block_time)
.bind(input.failed)
.bind(input.err_json.clone())
.fetch_optional(pool)
.await;
- return crate::postgres::query::core_queries::outcome_from_insert_optional(
- query_result,
- "postgres core transaction insert failed",
- );
+ return outcome_from_insert_optional(query_result, "postgres core transaction insert failed");
}
pub(crate) async fn insert_core_account_keys(
@@ -79,12 +38,12 @@ pub(crate) async fn insert_core_account_keys(
let mut inserted_count = 0_u64;
let mut skipped_count = 0_u64;
for input in inputs {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let account_index_result = crate::postgres::query::core_queries::sql_i32_from_u32(
+ let account_index_result = sql_i32_from_u32(
input.account_index,
"core account index does not fit into SQL INTEGER",
);
@@ -92,9 +51,9 @@ pub(crate) async fn insert_core_account_keys(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let source = crate::postgres::query::core_queries::account_key_source_to_sql(input.source);
+ let source = account_key_source_to_sql(input.source);
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, account_index) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ((SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, account_index) DO NOTHING RETURNING id",
)
.bind(input.signature.as_str())
.bind(slot)
@@ -106,10 +65,8 @@ pub(crate) async fn insert_core_account_keys(
.bind(input.executable)
.fetch_optional(pool)
.await;
- let outcome_result = crate::postgres::query::core_queries::outcome_from_insert_optional(
- query_result,
- "postgres core account key insert failed",
- );
+ let outcome_result =
+ outcome_from_insert_optional(query_result, "postgres core account key insert failed");
let outcome = match outcome_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -127,32 +84,36 @@ pub(crate) async fn insert_core_instructions(
let mut inserted_count = 0_u64;
let mut skipped_count = 0_u64;
for input in inputs {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let processing_state =
- crate::postgres::query::core_queries::instruction_processing_state_to_sql(
- input.processing_state,
- );
+ let stack_height_result = optional_sql_i32_from_u32(
+ input.stack_height,
+ "core instruction stack height does not fit into SQL INTEGER",
+ );
+ let stack_height = match stack_height_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
+ let processing_state = instruction_processing_state_to_sql(input.processing_state);
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ((SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id",
)
.bind(input.signature.as_str())
.bind(slot)
.bind(input.instruction_path.as_str())
.bind(input.program_id.as_str())
+ .bind(stack_height)
.bind(&input.accounts_json)
.bind(&input.payload_json)
.bind(input.payload_json_hash.as_deref())
.bind(processing_state)
.fetch_optional(pool)
.await;
- let outcome_result = crate::postgres::query::core_queries::outcome_from_insert_optional(
- query_result,
- "postgres core instruction insert failed",
- );
+ let outcome_result =
+ outcome_from_insert_optional(query_result, "postgres core instruction insert failed");
let outcome = match outcome_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -170,25 +131,36 @@ pub(crate) async fn insert_core_inner_instructions(
let mut inserted_count = 0_u64;
let mut skipped_count = 0_u64;
for input in inputs {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
+ let stack_height_result = optional_sql_i32_from_u32(
+ input.stack_height,
+ "core inner instruction stack height does not fit into SQL INTEGER",
+ );
+ let stack_height = match stack_height_result {
+ std::result::Result::Ok(value) => value,
+ std::result::Result::Err(error) => return std::result::Result::Err(error),
+ };
+ let processing_state = instruction_processing_state_to_sql(input.processing_state);
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, accounts_json, payload_json, payload_json_hash) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ((SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id",
)
.bind(input.signature.as_str())
.bind(slot)
.bind(input.parent_instruction_path.as_str())
.bind(input.instruction_path.as_str())
.bind(input.program_id.as_str())
+ .bind(stack_height)
.bind(&input.accounts_json)
.bind(&input.payload_json)
.bind(input.payload_json_hash.as_deref())
+ .bind(processing_state)
.fetch_optional(pool)
.await;
- let outcome_result = crate::postgres::query::core_queries::outcome_from_insert_optional(
+ let outcome_result = outcome_from_insert_optional(
query_result,
"postgres core inner instruction insert failed",
);
@@ -209,21 +181,19 @@ pub(crate) async fn insert_core_logs(
let mut inserted_count = 0_u64;
let mut skipped_count = 0_u64;
for input in inputs {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let log_index_result = crate::postgres::query::core_queries::sql_i32_from_u32(
- input.log_index,
- "core log index does not fit into SQL INTEGER",
- );
+ let log_index_result =
+ sql_i32_from_u32(input.log_index, "core log index does not fit into SQL INTEGER");
let log_index = match log_index_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7) ON CONFLICT (signature, log_index) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ((SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7) ON CONFLICT (signature, log_index) DO NOTHING RETURNING id",
)
.bind(input.signature.as_str())
.bind(slot)
@@ -234,10 +204,8 @@ pub(crate) async fn insert_core_logs(
.bind(input.log_text_hash.as_deref())
.fetch_optional(pool)
.await;
- let outcome_result = crate::postgres::query::core_queries::outcome_from_insert_optional(
- query_result,
- "postgres core log insert failed",
- );
+ let outcome_result =
+ outcome_from_insert_optional(query_result, "postgres core log insert failed");
let outcome = match outcome_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -255,12 +223,12 @@ pub(crate) async fn insert_core_balance_changes(
let mut inserted_count = 0_u64;
let mut skipped_count = 0_u64;
for input in inputs {
- let slot_result = crate::postgres::query::core_queries::sql_slot_from_u64(input.slot);
+ let slot_result = sql_slot_from_u64(input.slot);
let slot = match slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let balance_change_index_result = crate::postgres::query::core_queries::sql_i32_from_u32(
+ let balance_change_index_result = sql_i32_from_u32(
input.balance_change_index,
"core balance change index does not fit into SQL INTEGER",
);
@@ -268,7 +236,7 @@ pub(crate) async fn insert_core_balance_changes(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let account_index_result = crate::postgres::query::core_queries::optional_sql_i32_from_u32(
+ let account_index_result = optional_sql_i32_from_u32(
input.account_index,
"core balance account index does not fit into SQL INTEGER",
);
@@ -276,10 +244,9 @@ pub(crate) async fn insert_core_balance_changes(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let balance_kind =
- crate::postgres::query::core_queries::balance_change_kind_to_sql(input.balance_kind);
+ let balance_kind = balance_change_kind_to_sql(input.balance_kind);
let query_result = sqlx::query_scalar::(
- "INSERT INTO kb_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ON CONFLICT (signature, balance_change_index) DO NOTHING RETURNING id",
+ "INSERT INTO k_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ((SELECT id FROM k_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ON CONFLICT (signature, balance_change_index) DO NOTHING RETURNING id",
)
.bind(input.signature.as_str())
.bind(slot)
@@ -294,7 +261,7 @@ pub(crate) async fn insert_core_balance_changes(
.bind(input.delta_json.clone())
.fetch_optional(pool)
.await;
- let outcome_result = crate::postgres::query::core_queries::outcome_from_insert_optional(
+ let outcome_result = outcome_from_insert_optional(
query_result,
"postgres core balance change insert failed",
);
@@ -313,31 +280,25 @@ pub(crate) async fn list_core_instructions_for_replay(
filter: &crate::CoreInstructionReplayFilter,
page_request: &crate::PageRequest,
) -> ks_core::Result> {
- let min_slot_result =
- crate::postgres::query::core_queries::optional_sql_slot_from_u64(filter.min_slot);
+ let min_slot_result = optional_sql_slot_from_u64(filter.min_slot);
let min_slot = match min_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let max_slot_result =
- crate::postgres::query::core_queries::optional_sql_slot_from_u64(filter.max_slot);
+ let max_slot_result = optional_sql_slot_from_u64(filter.max_slot);
let max_slot = match max_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let offset_result = crate::postgres::query::core_queries::sql_i64_from_u64(
- page_request.offset,
- "page offset does not fit into SQL BIGINT",
- );
+ let offset_result =
+ sql_i64_from_u64(page_request.offset, "page offset does not fit into SQL BIGINT");
let offset = match offset_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let processing_state = filter
- .processing_state
- .map(crate::postgres::query::core_queries::instruction_processing_state_to_sql);
+ let processing_state = filter.processing_state.map(instruction_processing_state_to_sql);
let query_result = sqlx::query(
- "SELECT id, transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM kb_sol_core_instructions WHERE ($1::TEXT IS NULL OR processing_state = $1) AND ($2::TEXT IS NULL OR program_id = $2) AND ($3::BIGINT IS NULL OR slot >= $3) AND ($4::BIGINT IS NULL OR slot <= $4) ORDER BY slot ASC, id ASC LIMIT $5 OFFSET $6",
+ "SELECT id, transaction_id, signature, slot, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM k_sol_core_instructions WHERE ($1::TEXT IS NULL OR processing_state = $1) AND ($2::TEXT IS NULL OR program_id = $2) AND ($3::BIGINT IS NULL OR slot >= $3) AND ($4::BIGINT IS NULL OR slot <= $4) ORDER BY slot ASC, id ASC LIMIT $5 OFFSET $6",
)
.bind(processing_state)
.bind(filter.program_id.as_deref())
@@ -357,7 +318,7 @@ pub(crate) async fn list_core_instructions_for_replay(
};
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
- let mapped_result = crate::postgres::query::core_queries::row_to_core_instruction_row(&row);
+ let mapped_result = row_to_core_instruction_row(&row);
match mapped_result {
std::result::Result::Ok(value) => output.push(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -379,11 +340,7 @@ pub(crate) async fn list_core_instruction_replay_inputs(
};
let mut output = std::vec::Vec::with_capacity(instructions.len());
for instruction in instructions {
- let input_result = crate::postgres::query::core_queries::load_replay_input_for_instruction(
- pool,
- &instruction,
- )
- .await;
+ let input_result = load_replay_input_for_instruction(pool, &instruction).await;
match input_result {
std::result::Result::Ok(value) => output.push(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -396,14 +353,12 @@ pub(crate) async fn list_decode_replay_inputs(
pool: &sqlx::PgPool,
filter: &crate::DecodeSelectionFilter,
) -> ks_core::Result> {
- let min_slot_result =
- crate::postgres::query::core_queries::optional_sql_slot_from_u64(filter.min_slot);
+ let min_slot_result = optional_sql_slot_from_u64(filter.min_slot);
let min_slot = match min_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let max_slot_result =
- crate::postgres::query::core_queries::optional_sql_slot_from_u64(filter.max_slot);
+ let max_slot_result = optional_sql_slot_from_u64(filter.max_slot);
let max_slot = match max_slot_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -413,28 +368,23 @@ pub(crate) async fn list_decode_replay_inputs(
.processing_states
.iter()
.map(|state| {
- return crate::postgres::query::core_queries::instruction_processing_state_to_sql(
- *state,
- )
- .to_string();
+ return instruction_processing_state_to_sql(*state).to_string();
})
.collect::>();
let rows_result = if filter.incomplete_signatures {
- sqlx::query(
- "WITH incomplete AS (SELECT signature, MIN(slot) AS first_slot FROM kb_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (processing_state = ANY($2) OR (processing_state = 'ignored' AND lifecycle_reason = 'unsupported')) GROUP BY signature ORDER BY MIN(slot), signature LIMIT $7) SELECT i.id, i.transaction_id, i.signature, i.slot, i.instruction_path, i.program_id, i.accounts_json, i.payload_json, i.payload_json_hash, i.processing_state, i.created_at, i.updated_at FROM kb_sol_core_instructions i JOIN incomplete s ON s.signature = i.signature WHERE (cardinality($5::text[]) = 0 OR i.program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR i.instruction_path = ANY($6)) ORDER BY i.slot ASC, i.signature ASC, i.instruction_path ASC",
- )
- .bind(&filter.signatures)
- .bind(&processing_states)
- .bind(min_slot)
- .bind(max_slot)
- .bind(&filter.program_ids)
- .bind(&filter.instruction_paths)
- .bind(limit)
- .fetch_all(pool)
- .await
+ sqlx::query(INCOMPLETE_DECODE_INPUTS_SQL)
+ .bind(&filter.signatures)
+ .bind(&processing_states)
+ .bind(min_slot)
+ .bind(max_slot)
+ .bind(&filter.program_ids)
+ .bind(&filter.instruction_paths)
+ .bind(limit)
+ .fetch_all(pool)
+ .await
} else {
sqlx::query(
- "SELECT id, transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM kb_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND (cardinality($2::text[]) = 0 OR processing_state = ANY($2)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR instruction_path = ANY($6)) ORDER BY slot ASC, signature ASC, instruction_path ASC LIMIT $7",
+ "SELECT id, transaction_id, signature, slot, instruction_path, program_id, stack_height, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM k_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND (cardinality($2::text[]) = 0 OR processing_state = ANY($2)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR instruction_path = ANY($6)) ORDER BY slot ASC, signature ASC, instruction_path ASC LIMIT $7",
)
.bind(&filter.signatures)
.bind(&processing_states)
@@ -456,17 +406,12 @@ pub(crate) async fn list_decode_replay_inputs(
};
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
- let instruction_result =
- crate::postgres::query::core_queries::row_to_core_instruction_row(&row);
+ let instruction_result = row_to_core_instruction_row(&row);
let instruction = match instruction_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
- let input_result = crate::postgres::query::core_queries::load_replay_input_for_instruction(
- pool,
- &instruction,
- )
- .await;
+ let input_result = load_replay_input_for_instruction(pool, &instruction).await;
match input_result {
std::result::Result::Ok(value) => output.push(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -479,12 +424,9 @@ pub(crate) async fn update_core_instruction_lifecycle(
pool: &sqlx::PgPool,
mark: &crate::CoreInstructionLifecycleMark,
) -> ks_core::Result {
- let processing_state =
- crate::postgres::query::core_queries::instruction_processing_state_to_sql(
- mark.processing_state,
- );
+ let processing_state = instruction_processing_state_to_sql(mark.processing_state);
let query_result = sqlx::query(
- "UPDATE kb_sol_core_instructions SET processing_state = $1, processor_name = $2, processor_version = $3, lifecycle_reason = $4, updated_at = NOW() WHERE signature = $5 AND instruction_path = $6",
+ "UPDATE k_sol_core_instructions SET processing_state = $1, processor_name = $2, processor_version = $3, lifecycle_reason = $4, updated_at = NOW() WHERE signature = $5 AND instruction_path = $6",
)
.bind(processing_state)
.bind(mark.processor_name.as_deref())
@@ -494,7 +436,7 @@ pub(crate) async fn update_core_instruction_lifecycle(
.bind(mark.instruction_path.as_str())
.execute(pool)
.await;
- return crate::postgres::query::core_queries::outcome_from_update_result(
+ return outcome_from_update_result(
query_result,
"postgres core instruction lifecycle update failed",
);
@@ -549,10 +491,7 @@ fn balance_change_kind_to_sql(kind: crate::CoreBalanceChangeKind) -> &'static st
}
fn sql_slot_from_u64(slot: u64) -> ks_core::Result {
- return crate::postgres::query::core_queries::sql_i64_from_u64(
- slot,
- "solana slot does not fit into SQL BIGINT",
- );
+ return sql_i64_from_u64(slot, "solana slot does not fit into SQL BIGINT");
}
fn optional_sql_slot_from_u64(
@@ -560,7 +499,7 @@ fn optional_sql_slot_from_u64(
) -> ks_core::Result> {
return match slot {
std::option::Option::Some(value) => {
- let conversion_result = crate::postgres::query::core_queries::sql_slot_from_u64(value);
+ let conversion_result = sql_slot_from_u64(value);
match conversion_result {
std::result::Result::Ok(converted) => {
std::result::Result::Ok(std::option::Option::Some(converted))
@@ -586,8 +525,7 @@ fn optional_sql_i32_from_u32(
) -> ks_core::Result> {
return match value {
std::option::Option::Some(inner) => {
- let conversion_result =
- crate::postgres::query::core_queries::sql_i32_from_u32(inner, message);
+ let conversion_result = sql_i32_from_u32(inner, message);
match conversion_result {
std::result::Result::Ok(converted) => {
std::result::Result::Ok(std::option::Option::Some(converted))
@@ -599,6 +537,26 @@ fn optional_sql_i32_from_u32(
};
}
+fn optional_u32_from_sql_i32(
+ value: std::option::Option,
+ message: &str,
+) -> ks_core::Result> {
+ return match value {
+ std::option::Option::Some(inner) => {
+ let conversion_result = u32::try_from(inner);
+ match conversion_result {
+ std::result::Result::Ok(converted) => {
+ std::result::Result::Ok(std::option::Option::Some(converted))
+ },
+ std::result::Result::Err(_error) => {
+ std::result::Result::Err(ks_core::Error::db(message))
+ },
+ }
+ },
+ std::option::Option::None => std::result::Result::Ok(std::option::Option::None),
+ };
+}
+
fn sql_i64_from_u64(value: u64, message: &str) -> ks_core::Result