1use std::fmt;
2
3use powdb_storage::types::Value;
4
5#[derive(Debug)]
7pub enum QueryResult {
8 Rows {
9 columns: Vec<String>,
10 rows: Vec<Vec<Value>>,
11 },
12 Scalar(Value), Modified(u64), Created(String), Executed {
16 message: String,
17 }, }
19
20impl QueryResult {
21 pub fn row_count(&self) -> usize {
22 match self {
23 QueryResult::Rows { rows, .. } => rows.len(),
24 QueryResult::Scalar(_) => 1,
25 QueryResult::Modified(n) => *n as usize,
26 QueryResult::Created(_) => 0,
27 QueryResult::Executed { .. } => 0,
28 }
29 }
30}
31
32#[derive(Debug, Clone, PartialEq)]
39pub enum QueryError {
40 TableNotFound(String),
42 ColumnNotFound { table: String, column: String },
44 TypeError(String),
46 JoinLimitExceeded,
48 NestedLoopPairLimitExceeded {
51 left_rows: usize,
52 right_rows: usize,
53 limit: usize,
54 },
55 SortLimitExceeded,
57 MemoryLimitExceeded {
61 limit_bytes: usize,
62 requested_bytes: usize,
63 },
64 Parse(String),
66 IndexError(String),
68 ViewError(String),
70 StorageError(String),
72 ReadonlyNeedsWrite,
74 ReadonlyMode,
79 Timeout { timeout_ms: u64 },
84 Cancelled,
87 Execution(String),
89}
90
91impl fmt::Display for QueryError {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 QueryError::TableNotFound(t) => write!(f, "table '{t}' not found"),
95 QueryError::ColumnNotFound { table, column } => {
96 if table.is_empty() {
97 write!(f, "column '{column}' not found")
98 } else {
99 write!(f, "column '{column}' not found in table '{table}'")
100 }
101 }
102 QueryError::TypeError(msg) => write!(f, "type mismatch: {msg}"),
103 QueryError::JoinLimitExceeded => write!(f, "join result exceeds row limit"),
104 QueryError::NestedLoopPairLimitExceeded {
105 left_rows,
106 right_rows,
107 limit,
108 } => {
109 let pairs = left_rows.checked_mul(*right_rows);
110 match pairs {
111 Some(pairs) => write!(
112 f,
113 "nested-loop join would evaluate {pairs} candidate pairs, above the {limit} pair limit; add an equi-key to ON, index/filter an input, reduce the joined row counts, or raise the cap via POWDB_MAX_NESTED_LOOP_PAIRS"
114 ),
115 None => write!(
116 f,
117 "nested-loop join candidate count overflows usize ({left_rows} x {right_rows}), above the {limit} pair limit; add an equi-key to ON, index/filter an input, reduce the joined row counts, or raise the cap via POWDB_MAX_NESTED_LOOP_PAIRS"
118 ),
119 }
120 }
121 QueryError::SortLimitExceeded => {
122 write!(f, "sort input exceeds row limit — add a LIMIT clause")
123 }
124 QueryError::MemoryLimitExceeded {
125 limit_bytes,
126 requested_bytes,
127 } => write!(
128 f,
129 "query exceeded memory budget: requested {requested_bytes} bytes, limit {limit_bytes} bytes"
130 ),
131 QueryError::Parse(msg) => write!(f, "{msg}"),
132 QueryError::IndexError(msg) => write!(f, "{msg}"),
133 QueryError::ViewError(msg) => write!(f, "{msg}"),
134 QueryError::StorageError(msg) => write!(f, "{msg}"),
135 QueryError::ReadonlyNeedsWrite => {
136 write!(f, "__POWDB_READONLY_NEEDS_WRITE__")
137 }
138 QueryError::ReadonlyMode => write!(
139 f,
140 "readonly mode: statement requires a writer (this database was opened read-only for snapshot serving; refresh materialized views before snapshotting a read-only directory)"
141 ),
142 QueryError::Timeout { timeout_ms } => {
143 write!(f, "query timeout after {timeout_ms}ms")
144 }
145 QueryError::Cancelled => write!(f, "query cancelled by client disconnect"),
146 QueryError::Execution(msg) => write!(f, "{msg}"),
147 }
148 }
149}
150
151impl std::error::Error for QueryError {}
152
153impl From<String> for QueryError {
154 fn from(s: String) -> Self {
155 QueryError::Execution(s)
156 }
157}
158
159impl From<&str> for QueryError {
160 fn from(s: &str) -> Self {
161 QueryError::Execution(s.to_string())
162 }
163}