#![deny(clippy::wildcard_enum_match_arm)]
use common::{LeafError, internal_todo};
use surrealdb_types::{Error as TypesError, ToSql, ValidationError};
use crate::expr::Expr;
use crate::val::CoerceError;
#[derive(Debug, thiserror::Error)]
pub(crate) enum Error {
#[error("An error occurred: {0}")]
Thrown(String),
#[error("Specify a namespace to use")]
NsEmpty,
#[error("Specify a database to use")]
DbEmpty,
#[error("Invalid query: {message}")]
Query {
message: String,
},
#[error("'{name}' is a protected variable and cannot be set")]
InvalidParam {
name: String,
},
#[error("Found {} on FETCH CLAUSE, but FETCH expects an idiom, a string or fields", value.to_sql())]
InvalidFetch {
value: Expr,
},
#[error("Found {value} but the LIMIT clause must evaluate to a positive integer")]
InvalidLimit {
value: String,
},
#[error("Found {value} but the START clause must evaluate to a positive integer")]
InvalidStart {
value: String,
},
#[error("Problem with embedded script function. {message}")]
InvalidScript {
message: String,
},
#[error("Problem with machine learning computation. {message}")]
#[allow(dead_code)]
InvalidModel {
message: String,
},
#[error("There was a problem running the {name}() function. {message}")]
InvalidFunction {
name: String,
message: String,
},
#[error("Invalid control flow statement, break or continue statement found outside of loop.")]
InvalidControlFlow,
#[error("You don't have permission to change to the {ns} namespace")]
NsNotAllowed {
ns: String,
},
#[error("You don't have permission to change to the {db} database")]
DbNotAllowed {
db: String,
},
#[error("Reached excessive computation depth due to functions, subqueries, or computed values")]
ComputationDepthExceeded,
#[error("Invalid statement: {0}")]
InvalidStatement(String),
#[error("Cannot execute statement using value: {value}")]
InvalidStatementTarget {
value: String,
},
#[error("Cannot execute CREATE statement using value: {value}")]
CreateStatement {
value: String,
},
#[error("Cannot execute UPSERT statement using value: {value}")]
UpsertStatement {
value: String,
},
#[error("Cannot execute UPDATE statement using value: {value}")]
UpdateStatement {
value: String,
},
#[error("Cannot execute RELATE statement where property 'in' is: {value}")]
RelateStatementIn {
value: String,
},
#[error("Cannot execute RELATE statement where property 'id' is: {value}")]
RelateStatementId {
value: String,
},
#[error("Cannot execute RELATE statement where property 'out' is: {value}")]
RelateStatementOut {
value: String,
},
#[error("Cannot execute DELETE statement using value: {value}")]
DeleteStatement {
value: String,
},
#[error("Cannot execute INSERT statement using value: {value}")]
InsertStatement {
value: String,
},
#[error("Cannot execute INSERT statement where property 'in' is: {value}")]
InsertStatementIn {
value: String,
},
#[error("Cannot execute INSERT statement where property 'id' is: {value}")]
InsertStatementId {
value: String,
},
#[error("Cannot execute INSERT statement where property 'out' is: {value}")]
InsertStatementOut {
value: String,
},
#[error("Cannot execute LIVE statement using value: {value}")]
LiveStatement {
value: String,
},
#[error("Cannot execute KILL statement using id: {value}")]
KillStatement {
value: String,
},
#[error("Expected a single result output when using the ONLY keyword")]
SingleOnlyOutput,
#[error("You don't have permission to view the ${name} parameter")]
ParamPermissions {
name: String,
},
#[error("You don't have permission to run the {name} function")]
FunctionPermissions {
name: String,
},
#[error("A PERMISSIONS clause cannot contain a statement that modifies data")]
PermissionPredicateSideEffect,
#[error("A COMPUTED clause cannot contain a statement that modifies data")]
ComputedFieldSideEffect,
#[error("Tried to set `${name}`, but couldn't coerce value: {error}")]
SetCoerce {
name: String,
error: Box<CoerceError>,
},
#[error("Couldn't coerce return value from function `{name}`: {error}")]
ReturnCoerce {
name: String,
error: Box<CoerceError>,
},
#[error("Unimplemented functionality: {0}")]
Unimplemented(String),
#[error("Planner unsupported: {0}")]
PlannerUnsupported(String),
#[error("Planner not yet implemented: {0}")]
#[allow(
dead_code,
reason = "no planner path raises this today; the ladders that read it are already in place"
)]
PlannerUnimplemented(String),
#[error("The access method cannot be defined on the requested level")]
AccessLevelMismatch,
#[error(
"The ES512 algorithm is not currently supported. Please use ES384 or another supported algorithm"
)]
AccessUnsupportedAlgorithm,
#[error(
"The access method '{ac}' names the redaction placeholder as its key, which is published wherever an access is rendered; restore it from a definition carrying the real key, or define it again"
)]
AccessRedactedKey {
ac: String,
},
#[error(
"Tokens issued by record access methods can be consumed by third parties and must have an expiration; DURATION FOR TOKEN cannot be NONE on TYPE RECORD access"
)]
AccessRecordTokenDurationRequired,
#[error(
"A CONTEXT clause is evaluated on every authentication and its result is exposed to the client; it must be read-only"
)]
AccessContextNotReadOnly,
#[error(
"A CONTEXT clause is evaluated with the authenticated record's own permissions, so it can only be defined on an access method that authenticates a record"
)]
AccessContextRequiresRecord,
#[error("This access grant has an invalid subject")]
AccessGrantInvalidSubject,
#[error("This access grant has been revoked")]
AccessGrantRevoked,
#[error("Found {found} for bound but expected {expected}.")]
InvalidBound {
found: String,
expected: String,
},
#[error("Exceeded the idiom recursion limit of {limit}.")]
IdiomRecursionLimitExceeded {
limit: u32,
},
#[error("Tried to use a `@` repeat recurse symbol in a position where it is not supported")]
UnsupportedRepeatRecurse,
#[error("Cannot construct a recursion plan when an instruction is provided")]
RecursionInstructionPlanConflict,
#[error("Expected a record ID during recursive graph traversal, but found `{value}`")]
InvalidRecursionTarget {
value: String,
},
#[error(
"Cannot use the `REFERENCE` keyword with `TYPE {0}`. Specify only a `record` type, or a type containing only records, instead."
)]
ReferenceTypeConflict(String),
#[error(
"Cannot use the `REFERENCE` keyword on nested field `{0}`. Specify a referencing field at the root level instead."
)]
ReferenceNestedField(String),
#[error(
"Cannot set field `{name}` with type `{kind}` as it mismatched with field `{existing_name}` with type `{existing_kind}`"
)]
MismatchedFieldTypes {
name: String,
kind: String,
existing_name: String,
existing_kind: String,
},
#[error("Cannot use the `{0}` keyword with `COMPUTED`.")]
ComputedKeywordConflict(String),
#[error("Cannot define field `{0}` as `COMPUTED` since a nested field `{1}` already exists.")]
ComputedNestedFieldConflict(String, String),
#[error("Cannot define nested field `{0}` as parent field `{1}` is a `COMPUTED` field.")]
ComputedParentFieldConflict(String, String),
#[error("Cannot define field `{0}` as `COMPUTED` fields must be top-level.")]
ComputedNestedField(String),
#[error("Cannot define field `{0}` as `COMPUTED` bodies must be read-only.")]
ComputedWrite(String),
#[error("Cyclic dependency detected among field clauses: {0}")]
ComputedFieldCycle(String),
#[error("Cannot use the `{0}` keyword on the `id` field.")]
IdFieldKeywordConflict(String),
#[error("Cannot use the `{0}` type on the `id` field, as that's not a valid record id key.")]
IdFieldUnsupportedKind(String),
#[error("Computed fields cannot be indexed. Index: '{index}' - Field: '{field}'")]
ComputedFieldCannotBeIndexed {
field: String,
index: String,
},
}
impl LeafError for Error {
fn map_kind(self, message: String) -> TypesError {
match self {
Error::NsEmpty => TypesError::validation(message, ValidationError::NamespaceEmpty),
Error::DbEmpty => TypesError::validation(message, ValidationError::DatabaseEmpty),
Error::InvalidParam {
name,
} => TypesError::validation(
message,
ValidationError::InvalidParameter {
name,
},
),
Error::AccessUnsupportedAlgorithm => TypesError::validation(message, None),
Error::AccessRedactedKey {
..
} => TypesError::validation(message, None),
Error::ComputedWrite(_) | Error::ComputedFieldSideEffect => {
TypesError::validation(message, ValidationError::InvalidRequest)
}
Error::AccessContextNotReadOnly | Error::AccessContextRequiresRecord => {
TypesError::validation(message, ValidationError::InvalidRequest)
}
Error::Thrown(_) => TypesError::thrown(message),
Error::Unimplemented(_) => TypesError::internal(message),
Error::NsNotAllowed {
..
}
| Error::DbNotAllowed {
..
} => TypesError::not_allowed(message, None),
Error::Query {
..
}
| Error::InvalidFetch {
..
}
| Error::InvalidLimit {
..
}
| Error::InvalidStart {
..
}
| Error::InvalidScript {
..
}
| Error::InvalidModel {
..
}
| Error::InvalidFunction {
..
}
| Error::InvalidControlFlow
| Error::ComputationDepthExceeded
| Error::InvalidStatement(_)
| Error::InvalidStatementTarget {
..
}
| Error::CreateStatement {
..
}
| Error::UpsertStatement {
..
}
| Error::UpdateStatement {
..
}
| Error::RelateStatementIn {
..
}
| Error::RelateStatementId {
..
}
| Error::RelateStatementOut {
..
}
| Error::DeleteStatement {
..
}
| Error::InsertStatement {
..
}
| Error::InsertStatementIn {
..
}
| Error::InsertStatementId {
..
}
| Error::InsertStatementOut {
..
}
| Error::LiveStatement {
..
}
| Error::KillStatement {
..
}
| Error::SingleOnlyOutput
| Error::ParamPermissions {
..
}
| Error::FunctionPermissions {
..
}
| Error::PermissionPredicateSideEffect
| Error::SetCoerce {
..
}
| Error::ReturnCoerce {
..
}
| Error::PlannerUnsupported(_)
| Error::PlannerUnimplemented(_)
| Error::AccessLevelMismatch
| Error::AccessRecordTokenDurationRequired
| Error::AccessGrantInvalidSubject
| Error::AccessGrantRevoked
| Error::InvalidBound {
..
}
| Error::IdiomRecursionLimitExceeded {
..
}
| Error::UnsupportedRepeatRecurse
| Error::RecursionInstructionPlanConflict
| Error::InvalidRecursionTarget {
..
}
| Error::ReferenceTypeConflict(_)
| Error::ReferenceNestedField(_)
| Error::MismatchedFieldTypes {
..
}
| Error::ComputedKeywordConflict(_)
| Error::ComputedNestedFieldConflict(..)
| Error::ComputedParentFieldConflict(..)
| Error::ComputedNestedField(_)
| Error::ComputedFieldCycle(_)
| Error::IdFieldKeywordConflict(_)
| Error::IdFieldUnsupportedKind(_)
| Error::ComputedFieldCannotBeIndexed {
..
} => internal_todo(message),
}
}
}
impl From<Error> for crate::expr::ControlFlow {
fn from(error: Error) -> Self {
crate::expr::ControlFlow::Err(anyhow::Error::new(error))
}
}