Encode an array value PostgreSQL array binary format: - ndim (i32): number of dimensions - dataoffset (i32): offset to data, 0 if no NULLs - elemtype (i32): element type OID - For each dimension: - dim_size (i32): number of elements in this dimension - lower_bound (i32): lower bound (typically 1) - NULL bitmap (optional): bit array indicating NULL positions - Elements: each prefixed with length (i
(
array_str: &str,
elem_type_oid: i32,
)
| 272 | /// - NULL bitmap (optional): bit array indicating NULL positions |
| 273 | /// - Elements: each prefixed with length (i32), -1 for NULL |
| 274 | pub fn encode_array( |
| 275 | array_str: &str, |
| 276 | elem_type_oid: i32, |
| 277 | ) -> Result<Vec<u8>, PgSqliteError> { |
| 278 | // Input validation |
| 279 | if array_str.len() > MAX_STRING_LENGTH { |
| 280 | return Err(PgSqliteError::InvalidParameter( |
| 281 | format!("Array string too long: {} bytes (max: {})", array_str.len(), MAX_STRING_LENGTH) |
| 282 | )); |
| 283 | } |
| 284 | |
| 285 | debug!("encode_array called with: '{}', type_oid: {}", array_str, elem_type_oid); |
| 286 | |
| 287 | // Convert PostgreSQL format to JSON if needed |
| 288 | let json_str = Self::pg_array_to_json(array_str) |
| 289 | .map_err(|e| PgSqliteError::Protocol(format!("Array format conversion failed: {}", e)))?; |
| 290 | debug!("After conversion to JSON: '{}'", json_str); |
| 291 | |
| 292 | // Additional JSON validation |
| 293 | Self::validate_json_security(&json_str)?; |
| 294 | |
| 295 | // Parse JSON array |
| 296 | let array: serde_json::Value = serde_json::from_str(&json_str) |
| 297 | .map_err(|e| PgSqliteError::InvalidParameter(format!("Invalid JSON array: {e}")))?; |
| 298 | |
| 299 | let elements = array.as_array() |
| 300 | .ok_or_else(|| PgSqliteError::InvalidParameter("Not a JSON array".to_string()))?; |
| 301 | |
| 302 | // Array size validation |
| 303 | if elements.len() > MAX_ARRAY_SIZE { |
| 304 | return Err(PgSqliteError::InvalidParameter( |
| 305 | format!("Array too large: {} elements (max: {})", elements.len(), MAX_ARRAY_SIZE) |
| 306 | )); |
| 307 | } |
| 308 | |
| 309 | if elements.is_empty() { |
| 310 | // Empty array |
| 311 | let mut result = Vec::new(); |
| 312 | result.extend_from_slice(&0i32.to_be_bytes()); // ndim = 0 |
| 313 | result.extend_from_slice(&0i32.to_be_bytes()); // dataoffset = 0 |
| 314 | result.extend_from_slice(&elem_type_oid.to_be_bytes()); // elemtype |
| 315 | return Ok(result); |
| 316 | } |
| 317 | |
| 318 | // Check for NULLs |
| 319 | let has_nulls = elements.iter().any(|e| e.is_null()); |
| 320 | debug!("Array has {} elements, has_nulls: {}", elements.len(), has_nulls); |
| 321 | |
| 322 | let mut result = Vec::new(); |
| 323 | |
| 324 | // Header |
| 325 | result.extend_from_slice(&1i32.to_be_bytes()); // ndim = 1 (1D array) |
| 326 | |
| 327 | // flags field: 1 if array has nulls, 0 otherwise (PostgreSQL binary protocol format) |
| 328 | // Note: This is NOT dataoffset! The binary protocol uses a simple flag. |
| 329 | result.extend_from_slice(&(if has_nulls { 1i32 } else { 0i32 }).to_be_bytes()); |
| 330 | result.extend_from_slice(&elem_type_oid.to_be_bytes()); // elemtype |
| 331 |