1use std::{
5 fmt::{Display, Formatter},
6 mem,
7 ops::{Deref, DerefMut},
8};
9
10use serde::{Deserialize, Serialize, de, ser};
11
12mod diagnostic;
13pub mod r#macro;
14pub mod render;
15pub mod util;
16
17use std::{array::TryFromSliceError, error, fmt, num, string};
18
19use render::DefaultRenderer;
20
21use crate::{fragment::Fragment, value::r#type::Type};
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct OperatorChainEntry {
26 pub node_id: u64,
27 pub operator_name: String,
28 pub operator_version: String,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct Diagnostic {
33 pub code: String,
34 pub rql: Option<String>,
35 pub message: String,
36 pub column: Option<DiagnosticColumn>,
37 pub fragment: Fragment,
38 pub label: Option<String>,
39 pub help: Option<String>,
40 pub notes: Vec<String>,
41 pub cause: Option<Box<Diagnostic>>,
42 pub operator_chain: Option<Vec<OperatorChainEntry>>,
44}
45
46#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
47pub struct DiagnosticColumn {
48 pub name: String,
49 pub r#type: Type,
50}
51
52impl Default for Diagnostic {
53 fn default() -> Self {
54 Self {
55 code: String::new(),
56 rql: None,
57 message: String::new(),
58 column: None,
59 fragment: Fragment::None,
60 label: None,
61 help: None,
62 notes: Vec::new(),
63 cause: None,
64 operator_chain: None,
65 }
66 }
67}
68
69impl Display for Diagnostic {
70 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71 f.write_fmt(format_args!("{}", self.code))
72 }
73}
74
75impl Diagnostic {
76 pub fn with_rql(&mut self, rql: String) {
79 self.rql = Some(rql.clone());
80
81 if let Some(ref mut cause) = self.cause {
82 let mut updated_cause = mem::take(cause.as_mut());
83 updated_cause.with_rql(rql);
84 **cause = updated_cause;
85 }
86 }
87
88 pub fn with_fragment(&mut self, new_fragment: Fragment) {
91 self.fragment = new_fragment;
95
96 if let Some(ref mut cause) = self.cause {
97 cause.with_fragment(self.fragment.clone());
98 }
99 }
100
101 pub fn fragment(&self) -> Option<Fragment> {
104 match &self.fragment {
105 Fragment::Statement {
106 ..
107 } => Some(self.fragment.clone()),
108 _ => None,
109 }
110 }
111}
112
113pub trait IntoDiagnostic {
119 fn into_diagnostic(self) -> Diagnostic;
121}
122
123#[derive(Debug, Clone, PartialEq)]
124pub enum UnaryOp {
125 Not,
126}
127
128impl Display for UnaryOp {
129 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
130 match self {
131 UnaryOp::Not => f.write_str("NOT"),
132 }
133 }
134}
135
136#[derive(Debug, Clone, PartialEq)]
137pub enum BinaryOp {
138 Add,
139 Sub,
140 Mul,
141 Div,
142 Rem,
143 Equal,
144 NotEqual,
145 LessThan,
146 LessThanEqual,
147 GreaterThan,
148 GreaterThanEqual,
149 Between,
150}
151
152impl BinaryOp {
153 pub fn symbol(&self) -> &'static str {
154 match self {
155 BinaryOp::Add => "+",
156 BinaryOp::Sub => "-",
157 BinaryOp::Mul => "*",
158 BinaryOp::Div => "/",
159 BinaryOp::Rem => "%",
160 BinaryOp::Equal => "==",
161 BinaryOp::NotEqual => "!=",
162 BinaryOp::LessThan => "<",
163 BinaryOp::LessThanEqual => "<=",
164 BinaryOp::GreaterThan => ">",
165 BinaryOp::GreaterThanEqual => ">=",
166 BinaryOp::Between => "BETWEEN",
167 }
168 }
169}
170
171impl Display for BinaryOp {
172 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173 f.write_str(self.symbol())
174 }
175}
176
177#[derive(Debug, Clone, PartialEq)]
178pub enum LogicalOp {
179 Not,
180 And,
181 Or,
182 Xor,
183}
184
185impl Display for LogicalOp {
186 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
187 match self {
188 LogicalOp::Not => f.write_str("NOT"),
189 LogicalOp::And => f.write_str("AND"),
190 LogicalOp::Or => f.write_str("OR"),
191 LogicalOp::Xor => f.write_str("XOR"),
192 }
193 }
194}
195
196#[derive(Debug, Clone, PartialEq)]
197pub enum OperandCategory {
198 Number,
199 Text,
200 Temporal,
201 Uuid,
202}
203
204impl Display for OperandCategory {
205 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
206 match self {
207 OperandCategory::Number => f.write_str("number"),
208 OperandCategory::Text => f.write_str("text"),
209 OperandCategory::Temporal => f.write_str("temporal value"),
210 OperandCategory::Uuid => f.write_str("UUID"),
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq)]
216pub enum ConstraintKind {
217 Utf8MaxBytes {
218 actual: usize,
219 max: usize,
220 },
221 BlobMaxBytes {
222 actual: usize,
223 max: usize,
224 },
225 IntMaxBytes {
226 actual: usize,
227 max: usize,
228 },
229 UintMaxBytes {
230 actual: usize,
231 max: usize,
232 },
233 DecimalPrecision {
234 actual: u8,
235 max: u8,
236 },
237 DecimalScale {
238 actual: u8,
239 max: u8,
240 },
241 NoneNotAllowed {
242 column_type: Type,
243 },
244}
245
246#[derive(Debug, Clone, PartialEq)]
247pub enum TemporalKind {
248 InvalidDateFormat,
249 InvalidDateTimeFormat,
250 InvalidTimeFormat,
251 InvalidDurationFormat,
252 InvalidYear,
253 InvalidTimeComponentFormat {
254 component: String,
255 },
256 InvalidMonth,
257 InvalidDay,
258 InvalidHour,
259 InvalidMinute,
260 InvalidSecond,
261 InvalidFractionalSeconds,
262 InvalidDateValues,
263 InvalidTimeValues,
264 InvalidDurationCharacter,
265 IncompleteDurationSpecification,
266 InvalidUnitInContext {
267 unit: char,
268 in_time_part: bool,
269 },
270 InvalidDurationComponentValue {
271 unit: char,
272 },
273 UnrecognizedTemporalPattern,
274 EmptyDateComponent,
275 EmptyTimeComponent,
276 DuplicateDurationComponent {
277 component: char,
278 },
279 OutOfOrderDurationComponent {
280 component: char,
281 },
282 DateTimeOutOfRange,
283 DateTimeOverflow {
284 message: String,
285 },
286 DurationOverflow {
287 message: String,
288 },
289 DurationMixedSign {
290 days: i32,
291 nanos: i64,
292 },
293 TimeOverflow {
294 message: String,
295 },
296 DateOverflow {
297 message: String,
298 },
299}
300
301#[derive(Debug, Clone, PartialEq)]
302pub enum BlobEncodingKind {
303 InvalidHex,
304 InvalidBase64,
305 InvalidBase64Url,
306 InvalidBase58,
307 InvalidUtf8Sequence {
308 error: String,
309 },
310}
311
312#[derive(Debug, Clone, PartialEq)]
313pub enum AstErrorKind {
314 TokenizeError {
315 message: String,
316 },
317 UnexpectedEof,
318 ExpectedIdentifier,
319 InvalidColumnProperty,
320 InvalidPolicy,
321 UnexpectedToken {
322 expected: String,
323 },
324 UnsupportedToken,
325 MultipleExpressionsWithoutBraces,
326 UnrecognizedType,
327 UnsupportedAstNode {
328 node_type: String,
329 },
330 EmptyPipeline,
331}
332
333#[derive(Debug, Clone, PartialEq)]
334pub enum ProcedureErrorKind {
335 UndefinedProcedure {
336 name: String,
337 },
338 NoRegisteredImplementation {
343 name: String,
344 },
345}
346
347#[derive(Debug, Clone, PartialEq)]
348pub enum RuntimeErrorKind {
349 VariableNotFound {
350 name: String,
351 },
352 VariableIsDataframe {
353 name: String,
354 },
355 VariableIsImmutable {
356 name: String,
357 },
358 BreakOutsideLoop,
359 ContinueOutsideLoop,
360 MaxIterationsExceeded {
361 limit: usize,
362 },
363 UndefinedFunction {
364 name: String,
365 },
366 FieldNotFound {
367 variable: String,
368 field: String,
369 available: Vec<String>,
370 },
371 AppendTargetNotFrame {
372 name: String,
373 },
374 AppendColumnMismatch {
375 name: String,
376 existing: Vec<String>,
377 incoming: Vec<String>,
378 fragment: Fragment,
379 },
380 ExpectedSingleColumn {
381 actual: usize,
382 },
383}
384
385#[derive(Debug, Clone, PartialEq)]
386pub enum NetworkErrorKind {
387 Connection {
388 message: String,
389 },
390 Engine {
391 message: String,
392 },
393 Transport {
394 message: String,
395 },
396 Status {
397 message: String,
398 },
399}
400
401#[derive(Debug, Clone, PartialEq)]
402pub enum AuthErrorKind {
403 AuthenticationFailed {
404 reason: String,
405 },
406 AuthorizationDenied {
407 resource: String,
408 },
409 TokenExpired,
410 InvalidToken,
411}
412
413#[derive(Debug, Clone, PartialEq)]
414pub enum FunctionErrorKind {
415 UnknownFunction,
416 ArityMismatch {
417 expected: usize,
418 actual: usize,
419 },
420 TooManyArguments {
421 max_args: usize,
422 actual: usize,
423 },
424 InvalidArgumentType {
425 index: usize,
426 expected: Vec<Type>,
427 actual: Type,
428 },
429 UndefinedArgument {
430 index: usize,
431 },
432 MissingInput,
433 ExecutionFailed {
434 reason: String,
435 },
436 InternalError {
437 details: String,
438 },
439 GeneratorNotFound,
440}
441
442#[derive(Debug, thiserror::Error)]
443pub enum TypeError {
444 #[error("Cannot apply {operator} operator to {operand_category}")]
445 LogicalOperatorNotApplicable {
446 operator: LogicalOp,
447 operand_category: OperandCategory,
448 fragment: Fragment,
449 },
450
451 #[error("Cannot apply '{operator}' operator to {left} and {right}")]
452 BinaryOperatorNotApplicable {
453 operator: BinaryOp,
454 left: Type,
455 right: Type,
456 fragment: Fragment,
457 },
458
459 #[error("unsupported cast from {from} to {to}")]
460 UnsupportedCast {
461 from: Type,
462 to: Type,
463 fragment: Fragment,
464 },
465
466 #[error("failed to cast to {target}")]
467 CastToNumberFailed {
468 target: Type,
469 fragment: Fragment,
470 cause: Box<TypeError>,
471 },
472
473 #[error("failed to cast to {target}")]
474 CastToTemporalFailed {
475 target: Type,
476 fragment: Fragment,
477 cause: Box<TypeError>,
478 },
479
480 #[error("failed to cast to bool")]
481 CastToBooleanFailed {
482 fragment: Fragment,
483 cause: Box<TypeError>,
484 },
485
486 #[error("failed to cast to {target}")]
487 CastToUuidFailed {
488 target: Type,
489 fragment: Fragment,
490 cause: Box<TypeError>,
491 },
492
493 #[error("failed to cast BLOB to UTF8")]
494 CastBlobToUtf8Failed {
495 fragment: Fragment,
496 cause: Box<TypeError>,
497 },
498
499 #[error("{message}")]
500 ConstraintViolation {
501 kind: ConstraintKind,
502 message: String,
503 fragment: Fragment,
504 },
505
506 #[error("invalid number format")]
507 InvalidNumberFormat {
508 target: Type,
509 fragment: Fragment,
510 },
511
512 #[error("number out of range")]
513 NumberOutOfRange {
514 target: Type,
515 fragment: Fragment,
516 descriptor: Option<NumberOutOfRangeDescriptor>,
517 },
518
519 #[error("NaN not allowed")]
520 NanNotAllowed,
521
522 #[error("too large for precise float conversion")]
523 IntegerPrecisionLoss {
524 shape_type: Type,
525 target: Type,
526 fragment: Fragment,
527 },
528
529 #[error("decimal scale exceeds precision")]
530 DecimalScaleExceedsPrecision {
531 scale: u8,
532 precision: u8,
533 fragment: Fragment,
534 },
535
536 #[error("invalid decimal precision")]
537 DecimalPrecisionInvalid {
538 precision: u8,
539 },
540
541 #[error("invalid boolean format")]
542 InvalidBooleanFormat {
543 fragment: Fragment,
544 },
545
546 #[error("empty boolean value")]
547 EmptyBooleanValue {
548 fragment: Fragment,
549 },
550
551 #[error("invalid boolean")]
552 InvalidNumberBoolean {
553 fragment: Fragment,
554 },
555
556 #[error("{message}")]
557 Temporal {
558 kind: TemporalKind,
559 message: String,
560 fragment: Fragment,
561 },
562
563 #[error("invalid UUID v4 format")]
564 InvalidUuid4Format {
565 fragment: Fragment,
566 },
567
568 #[error("invalid UUID v7 format")]
569 InvalidUuid7Format {
570 fragment: Fragment,
571 },
572
573 #[error("{message}")]
574 BlobEncoding {
575 kind: BlobEncodingKind,
576 message: String,
577 fragment: Fragment,
578 },
579
580 #[error("Serde deserialization error: {message}")]
581 SerdeDeserialize {
582 message: String,
583 },
584
585 #[error("Serde serialization error: {message}")]
586 SerdeSerialize {
587 message: String,
588 },
589
590 #[error("Keycode serialization error: {message}")]
591 SerdeKeycode {
592 message: String,
593 },
594
595 #[error("Array conversion error: {message}")]
596 ArrayConversion {
597 message: String,
598 },
599
600 #[error("UTF-8 conversion error: {message}")]
601 Utf8Conversion {
602 message: String,
603 },
604
605 #[error("Integer conversion error: {message}")]
606 IntegerConversion {
607 message: String,
608 },
609
610 #[error("{message}")]
611 Network {
612 kind: NetworkErrorKind,
613 message: String,
614 },
615
616 #[error("{message}")]
617 Auth {
618 kind: AuthErrorKind,
619 message: String,
620 },
621
622 #[error("dictionary entry ID {value} exceeds maximum {max_value} for type {id_type}")]
623 DictionaryCapacityExceeded {
624 id_type: Type,
625 value: u128,
626 max_value: u128,
627 },
628
629 #[error("{message}")]
630 AssertionFailed {
631 fragment: Fragment,
632 message: String,
633 expression: Option<String>,
634 },
635
636 #[error("{message}")]
637 Function {
638 kind: FunctionErrorKind,
639 message: String,
640 fragment: Fragment,
641 },
642
643 #[error("{message}")]
644 Ast {
645 kind: AstErrorKind,
646 message: String,
647 fragment: Fragment,
648 },
649
650 #[error("{message}")]
651 Runtime {
652 kind: RuntimeErrorKind,
653 message: String,
654 },
655
656 #[error("{message}")]
657 Procedure {
658 kind: ProcedureErrorKind,
659 message: String,
660 fragment: Fragment,
661 },
662}
663
664#[derive(Debug, Clone, PartialEq)]
665pub struct NumberOutOfRangeDescriptor {
666 pub namespace: Option<String>,
667 pub table: Option<String>,
668 pub column: Option<String>,
669 pub column_type: Option<Type>,
670}
671
672impl NumberOutOfRangeDescriptor {
673 pub fn location_string(&self) -> String {
674 match (self.namespace.as_deref(), self.table.as_deref(), self.column.as_deref()) {
675 (Some(s), Some(t), Some(c)) => format!("{}::{}.{}", s, t, c),
676 (Some(s), Some(t), None) => format!("{}::{}", s, t),
677 (None, Some(t), Some(c)) => format!("{}.{}", t, c),
678 (Some(s), None, Some(c)) => format!("{}::{}", s, c),
679 (Some(s), None, None) => s.to_string(),
680 (None, Some(t), None) => t.to_string(),
681 (None, None, Some(c)) => c.to_string(),
682 (None, None, None) => "unknown location".to_string(),
683 }
684 }
685}
686
687#[derive(Debug, PartialEq)]
688pub struct Error(pub Box<Diagnostic>);
689
690impl Deref for Error {
691 type Target = Diagnostic;
692
693 fn deref(&self) -> &Self::Target {
694 &self.0
695 }
696}
697
698impl DerefMut for Error {
699 fn deref_mut(&mut self) -> &mut Self::Target {
700 &mut self.0
701 }
702}
703
704impl Display for Error {
705 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
706 let out = DefaultRenderer::render_string(&self.0);
707 f.write_str(out.as_str())
708 }
709}
710
711impl Error {
712 pub fn diagnostic(self) -> Diagnostic {
713 *self.0
714 }
715}
716
717impl error::Error for Error {}
718
719impl de::Error for Error {
720 fn custom<T: Display>(msg: T) -> Self {
721 TypeError::SerdeDeserialize {
722 message: msg.to_string(),
723 }
724 .into()
725 }
726}
727
728impl ser::Error for Error {
729 fn custom<T: Display>(msg: T) -> Self {
730 TypeError::SerdeSerialize {
731 message: msg.to_string(),
732 }
733 .into()
734 }
735}
736
737impl From<num::TryFromIntError> for Error {
738 fn from(err: num::TryFromIntError) -> Self {
739 TypeError::IntegerConversion {
740 message: err.to_string(),
741 }
742 .into()
743 }
744}
745
746impl From<TryFromSliceError> for Error {
747 fn from(err: TryFromSliceError) -> Self {
748 TypeError::ArrayConversion {
749 message: err.to_string(),
750 }
751 .into()
752 }
753}
754
755impl From<string::FromUtf8Error> for Error {
756 fn from(err: string::FromUtf8Error) -> Self {
757 TypeError::Utf8Conversion {
758 message: err.to_string(),
759 }
760 .into()
761 }
762}
763
764impl From<TypeError> for Error {
765 fn from(err: TypeError) -> Self {
766 Error(Box::new(err.into_diagnostic()))
767 }
768}
769
770impl From<Box<TypeError>> for Error {
771 fn from(err: Box<TypeError>) -> Self {
772 Error(Box::new(err.into_diagnostic()))
773 }
774}
775
776impl From<num::TryFromIntError> for TypeError {
777 fn from(err: num::TryFromIntError) -> Self {
778 TypeError::IntegerConversion {
779 message: err.to_string(),
780 }
781 }
782}
783
784impl From<TryFromSliceError> for TypeError {
785 fn from(err: TryFromSliceError) -> Self {
786 TypeError::ArrayConversion {
787 message: err.to_string(),
788 }
789 }
790}
791
792impl From<string::FromUtf8Error> for TypeError {
793 fn from(err: string::FromUtf8Error) -> Self {
794 TypeError::Utf8Conversion {
795 message: err.to_string(),
796 }
797 }
798}
799
800impl de::Error for TypeError {
801 fn custom<T: Display>(msg: T) -> Self {
802 TypeError::SerdeDeserialize {
803 message: msg.to_string(),
804 }
805 }
806}
807
808impl ser::Error for TypeError {
809 fn custom<T: Display>(msg: T) -> Self {
810 TypeError::SerdeSerialize {
811 message: msg.to_string(),
812 }
813 }
814}