1pub(crate) mod bundled_csr;
43pub(crate) mod csr_shared;
44pub mod csr_trait;
45pub mod csr_variant;
46pub mod csr_with_properties;
47pub mod edge_table;
48pub mod fragmentation_stats;
49pub mod immutable_csr;
50pub mod mutable_csr;
51pub mod node_group;
52pub mod property_schema;
53pub(crate) mod pure_csr;
54pub mod record_form_policy;
55pub mod schema;
56pub mod single_mutable_csr;
57pub mod slot;
58
59pub use csr_trait::{CsrBase, MutableCsrTrait};
60pub use csr_variant::{CsrRowIter, CsrVariant};
61pub use csr_with_properties::CsrWithProperties;
62pub use edge_table::core::UpdateEdgePropertyByKeyParams;
63pub use edge_table::{EdgeIndexStatus, EdgeStore, EdgeWalRecoveryMode, IncidentDeletedEdge};
64pub use fragmentation_stats::{
65 FragmentationStats, VertexFragmentation, GROUP_FRAGMENTATION_THRESHOLD,
66};
67pub use linkrs_core::types::EdgeStrategy;
68use linkrs_core::types::{EdgeId, Timestamp, VertexId};
69use linkrs_core::Value;
70pub use mutable_csr::{EdgePosition, MutableCsr, MutableCsrIterator};
71pub use node_group::{
72 region_id_for_local, region_local_range, regions_per_group, CsrShardSet, EdgeCheckpointKind,
73 FreezeBlockReason, FreezeFeasibility, GroupDirty, NodeGroupStats, RegionDirty,
74 RegionMergeScope, ShardCsrIterator, TableShardManifest, DEFAULT_NODE_GROUP_BITS,
75 GROUP_MERGE_MIN_DENSITY, LEAF_REGION_ROWS, REGION_MERGE_MIN_DENSITY,
76};
77pub use single_mutable_csr::{SingleMutableCsr, SingleMutableCsrIterator};
78
79pub use bundled_csr::{decode_scalar, encode_scalar, BundledCsr};
80pub use edge_table::checkpoint::snapshot::{
81 MappedFrozen, MappedFrozenIterator, MappedFrozenRowIter,
82};
83pub use immutable_csr::{FrozenRowIter, ImmutableCsr, ImmutableCsrIterator};
84pub use linkrs_core::types::INVALID_EDGE_ID;
85pub use pure_csr::{PureAllIter, PureRowIter, PureTopologyCsr};
86
87pub use record_form_policy::{
88 bundled_ineligibility_reason, inline_form_accepts_rank, is_bundled_eligible,
89 is_scalar_encodable, validate_record_form_target, validate_strategy_form, RecordForm,
90 RecordFormPreference, BUNDLED_RANK_REQUIRES_COLUMNAR_MSG,
91};
92pub(crate) use record_form_policy::{
93 INLINE_FORM_SCHEMA_CHANGE_MSG, NO_EDGES_STORED_MSG, ROW_POSITION_CROSS_VARIANT_MSG,
94 SCHEMA_CHANGE_PENDING_MSG, SINGLE_REQUIRES_COLUMNAR_MSG,
95};
96pub use schema::{EdgeMultiplicity, EdgeRecord, EdgeSchema, IndexConsistency, StorageDirection};
97pub use slot::{ColdStamps, HotNbr, Nbr};
98
99pub type BatchInsertEntry<'a> = (u32, u32, i64, &'a [(std::sync::Arc<str>, Value)], Timestamp);
101
102pub type EdgePut = (u32, i64, EdgeId, Timestamp);
104
105pub type RowEdgeBatch = (u32, Vec<EdgePut>);
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::types::StoragePropertyDef;
112
113 #[test]
114 fn hot_half_stays_within_single_cache_line_budget() {
115 assert_eq!(std::mem::size_of::<HotNbr>(), 24);
123 assert_eq!(std::mem::size_of::<ColdStamps>(), 8);
124 assert_eq!(std::mem::size_of::<Nbr>(), 32);
125 }
126
127 #[test]
128 fn slot_halves_roundtrip_through_nbr() {
129 let nbr = Nbr::with_create_ts(7, 2, EdgeId(9), 100);
130 let assembled = Nbr::from_parts(nbr.hot(), nbr.cold());
131 assert_eq!(assembled, nbr);
132 assert_eq!(
133 Nbr::from_parts(HotNbr::dead_gap(), ColdStamps::dead_gap()),
134 Nbr::dead_gap()
135 );
136 }
137
138 #[test]
139 fn test_edge_schema_validation_both_none() {
140 let schema = EdgeSchema {
141 label_id: 0,
142 label_name: "invalid_edge".into(),
143 src_label: 0,
144 dst_label: 0,
145 properties: vec![],
146 oe_strategy: EdgeStrategy::None,
147 ie_strategy: EdgeStrategy::None,
148 schema_version: 1,
149 record_form: RecordForm::default(),
150 };
151
152 let result = schema.validate();
153 assert!(result.is_err());
154 assert!(result.unwrap_err().to_string().contains("at least one"));
155 }
156
157 #[test]
158 fn test_edge_schema_validation_both_enabled() {
159 let schema = EdgeSchema {
160 label_id: 0,
161 label_name: "valid_edge".into(),
162 src_label: 0,
163 dst_label: 0,
164 properties: vec![],
165 oe_strategy: EdgeStrategy::Multiple,
166 ie_strategy: EdgeStrategy::Single,
167 schema_version: 1,
168 record_form: RecordForm::default(),
169 };
170
171 let result = schema.validate();
172 assert!(result.is_ok());
173 }
174
175 #[test]
176 fn pure_target_rejection_comes_from_the_shared_constant() {
177 use linkrs_core::DataType;
178 let props = vec![StoragePropertyDef {
179 name: "p".into(),
180 data_type: DataType::Double,
181 nullable: true,
182 default_value: None,
183 }];
184 let err = validate_record_form_target(
185 &props,
186 EdgeStrategy::Multiple,
187 EdgeStrategy::Multiple,
188 RecordForm::Pure,
189 )
190 .expect_err("pure target with properties must be rejected");
191 assert!(
192 err.to_string()
193 .contains(record_form_policy::PURE_REQUIRES_ZERO_PROPERTIES_MSG),
194 "unexpected wording: {}",
195 err
196 );
197 }
198
199 #[test]
200 fn test_edge_schema_validation_ie_only() {
201 let schema = EdgeSchema {
202 label_id: 0,
203 label_name: "valid_edge".into(),
204 src_label: 0,
205 dst_label: 0,
206 properties: vec![],
207 oe_strategy: EdgeStrategy::None,
208 ie_strategy: EdgeStrategy::Multiple,
209 schema_version: 1,
210 record_form: RecordForm::default(),
211 };
212
213 let result = schema.validate();
214 assert!(result.is_ok());
215 assert_eq!(schema.storage_direction(), StorageDirection::InOnly);
216 assert!(!schema.has_out());
217 assert!(schema.has_in());
218 }
219}