vibesql_storage/database/point_lookup.rs
1// ============================================================================
2// Direct Point Lookup API (Performance Optimization)
3// ============================================================================
4//
5// This module provides high-performance point lookup methods that bypass
6// SQL parsing for direct primary key access.
7
8use super::Database;
9use crate::{Row, StorageError};
10
11impl Database {
12 // ============================================================================
13 // Direct Point Lookup API (Performance Optimization)
14 // ============================================================================
15
16 /// Get a row by primary key value - bypasses SQL parsing for maximum performance
17 ///
18 /// This method provides O(1) point lookups directly using the primary key index,
19 /// completely bypassing SQL parsing and the query execution pipeline.
20 ///
21 /// # Arguments
22 /// * `table_name` - Name of the table
23 /// * `pk_value` - Primary key value to look up
24 ///
25 /// # Returns
26 /// * `Ok(Some(&Row))` - The row if found
27 /// * `Ok(None)` - If no row matches the primary key
28 /// * `Err(StorageError)` - If table doesn't exist or has no primary key
29 ///
30 /// # Performance
31 /// This is ~100-300x faster than executing a SQL point SELECT query because it:
32 /// - Skips SQL parsing (~300µs)
33 /// - Skips query planning and optimization
34 /// - Uses direct HashMap lookup on the PK index
35 ///
36 /// # Example
37 /// ```text
38 /// let row = db.get_row_by_pk("users", &SqlValue::Integer(42))?;
39 /// if let Some(row) = row {
40 /// let name = &row.values[1];
41 /// }
42 /// ```
43 pub fn get_row_by_pk(
44 &self,
45 table_name: &str,
46 pk_value: &vibesql_types::SqlValue,
47 ) -> Result<Option<&Row>, StorageError> {
48 let table = self
49 .get_table(table_name)
50 .ok_or_else(|| StorageError::TableNotFound(table_name.to_string()))?;
51
52 let pk_index = table.primary_key_index().ok_or_else(|| {
53 StorageError::Other(format!("Table '{}' has no primary key", table_name))
54 })?;
55
56 // Look up the row index using the PK value
57 let key = vec![pk_value.clone()];
58 if let Some(&row_index) = pk_index.get(&key) {
59 let rows = table.scan();
60 if row_index < rows.len() {
61 return Ok(Some(&rows[row_index]));
62 }
63 }
64
65 Ok(None)
66 }
67
68 /// Get a specific column value by primary key - bypasses SQL parsing for maximum performance
69 ///
70 /// This is even faster than `get_row_by_pk` when you only need one column value,
71 /// as it avoids returning the entire row.
72 ///
73 /// # Arguments
74 /// * `table_name` - Name of the table
75 /// * `pk_value` - Primary key value to look up
76 /// * `column_index` - Index of the column to retrieve (0-based)
77 ///
78 /// # Returns
79 /// * `Ok(Some(&SqlValue))` - The column value if found
80 /// * `Ok(None)` - If no row matches the primary key
81 /// * `Err(StorageError)` - If table doesn't exist or column index is out of bounds
82 pub fn get_column_by_pk(
83 &self,
84 table_name: &str,
85 pk_value: &vibesql_types::SqlValue,
86 column_index: usize,
87 ) -> Result<Option<&vibesql_types::SqlValue>, StorageError> {
88 let table = self
89 .get_table(table_name)
90 .ok_or_else(|| StorageError::TableNotFound(table_name.to_string()))?;
91
92 // Validate column index
93 if column_index >= table.schema.columns.len() {
94 return Err(StorageError::Other(format!(
95 "Column index {} out of bounds for table '{}' with {} columns",
96 column_index,
97 table_name,
98 table.schema.columns.len()
99 )));
100 }
101
102 let pk_index = table.primary_key_index().ok_or_else(|| {
103 StorageError::Other(format!("Table '{}' has no primary key", table_name))
104 })?;
105
106 // Look up the row index using the PK value
107 let key = vec![pk_value.clone()];
108 if let Some(&row_index) = pk_index.get(&key) {
109 let rows = table.scan();
110 if row_index < rows.len() {
111 return Ok(rows[row_index].values.get(column_index));
112 }
113 }
114
115 Ok(None)
116 }
117
118 /// Get a row by composite primary key - for tables with multi-column primary keys
119 ///
120 /// # Arguments
121 /// * `table_name` - Name of the table
122 /// * `pk_values` - Primary key values in column order
123 ///
124 /// # Returns
125 /// * `Ok(Some(&Row))` - The row if found
126 /// * `Ok(None)` - If no row matches the primary key
127 /// * `Err(StorageError)` - If table doesn't exist or has no primary key
128 pub fn get_row_by_composite_pk(
129 &self,
130 table_name: &str,
131 pk_values: &[vibesql_types::SqlValue],
132 ) -> Result<Option<&Row>, StorageError> {
133 let table = self
134 .get_table(table_name)
135 .ok_or_else(|| StorageError::TableNotFound(table_name.to_string()))?;
136
137 let pk_index = table.primary_key_index().ok_or_else(|| {
138 StorageError::Other(format!("Table '{}' has no primary key", table_name))
139 })?;
140
141 // Look up the row index using the composite PK
142 let key: Vec<vibesql_types::SqlValue> = pk_values.to_vec();
143 if let Some(&row_index) = pk_index.get(&key) {
144 let rows = table.scan();
145 if row_index < rows.len() {
146 return Ok(Some(&rows[row_index]));
147 }
148 }
149
150 Ok(None)
151 }
152}