1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Error types shared between the query and SQL layers.
use super::FieldType;
/// Error raised while building or compiling a `QuerySet`.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum QueryError {
#[error("model `{model}` has no field `{field}`")]
UnknownField { model: &'static str, field: String },
#[error("field `{model}.{field}` is type {expected}, but the bound value is type {actual}")]
TypeMismatch {
model: &'static str,
field: String,
expected: FieldType,
actual: FieldType,
},
#[error("field `{model}.{field}` exceeds max_length {max} (got {actual})")]
MaxLengthExceeded {
model: &'static str,
field: String,
max: u32,
actual: u32,
},
#[error(
"field `{model}.{field}` value {value} is out of range (min = {min:?}, max = {max:?})"
)]
OutOfRange {
model: &'static str,
field: String,
value: i64,
min: Option<i64>,
max: Option<i64>,
},
/// `QuerySet::select_related("foo")` couldn't be lowered: the
/// field doesn't exist, isn't a `ForeignKey<T>`, the target
/// table isn't registered in `inventory`, or the target has no
/// primary key. Slice 9.0d.
#[error("select_related(`{field}`) on model `{model}` is invalid: {reason}")]
SelectRelatedInvalid {
model: &'static str,
field: String,
reason: String,
},
/// `AggregateBuilder::filter(alias, op, value)` was called with
/// an `op` that doesn't compose against an aggregate LHS via
/// [`crate::core::WhereExpr::ExprCompare`]. After issue #87,
/// the supported set is the binary-comparison ops (`Eq`/`Ne`/
/// `Lt`/`Lte`/`Gt`/`Gte`) plus the SQL-92 standard predicates
/// (`In`/`NotIn`, `Between`, `IsNull`, `Like`/`NotLike`,
/// `ILike`/`NotILike`). The JSON-op family + null-safe equality
/// (`IsDistinctFrom` / `IsNotDistinctFrom`) still need
/// dialect-specific writers that take a `&str` for the LHS,
/// so they're rejected here.
///
/// Drop into [`crate::query::AggregateBuilder::having`] with a
/// pre-built `WhereExpr` if you really need one of the
/// remaining ops against an aggregate.
#[error(
"HAVING auto-routing for annotation alias `{alias}` doesn't support \
{op:?} (JSON-op family + null-safe equality). Build a `WhereExpr` \
directly and pass it through `AggregateBuilder::having`."
)]
HavingOpNotSupported { alias: String, op: super::Op },
/// Django-shape `.filter("field__lookup", value)` got a lookup
/// suffix the parser doesn't recognize. Issue #71. The supported
/// set (exact / iexact / contains / icontains / startswith /
/// istartswith / endswith / iendswith / gt / gte / lt / lte / ne
/// / in / isnull / between / range) is documented on
/// [`crate::query::QuerySet::filter`]. Chained lookups
/// (`author__name__icontains`) aren't supported in v1.
#[error(
"unknown lookup suffix `__{suffix}` on field `{field}` — \
supported: exact, iexact, contains, icontains, startswith, \
istartswith, endswith, iendswith, gt, gte, lt, lte, ne, in, \
isnull, between, range, regex, iregex, trigram_similar, \
trigram_word_similar, search, array_contains, \
array_contained_by, array_overlap, range_contains, \
range_contained_by, range_overlap, range_strictly_left, \
range_strictly_right, range_adjacent"
)]
UnknownLookup { field: String, suffix: String },
/// Django-shape `.filter("field__lookup", value)` got a value
/// whose shape doesn't fit the chosen lookup. Issue #71.
/// Examples: `__in` with a non-list, `__isnull` with a
/// non-bool, `__between` with a list that isn't exactly 2
/// elements.
#[error(
"lookup `__{suffix}` on field `{field}` requires {expected}; \
got a value of shape {actual}"
)]
InvalidLookupValue {
field: String,
suffix: String,
expected: &'static str,
actual: &'static str,
},
/// `.values(cols)` was called without a subsequent aggregating
/// `.annotate(name, ...)`. Issue #75 v1 supports `.values()` only
/// as a GROUP BY hint paired with an aggregate annotation; pure
/// projection (returning `Vec<HashMap<String, SqlValue>>` with
/// only the requested columns) needs a separate writer path and
/// is queued for a follow-up. Until then, use the typed
/// `QuerySet::fetch(...)` path to read whole rows, or build an
/// `AggregateQuery` directly if you need a custom SELECT shape.
#[error(
"AggregateBuilder::values({cols:?}) requires at least one \
aggregating annotation (Count / Sum / Avg / Max / Min / \
StdDev / Variance). For pure projection (no GROUP BY) use \
`QuerySet::values_dict` / `values_list` / `values_list_flat` \
instead (issue #22)."
)]
ValuesRequiresAggregate { cols: Vec<&'static str> },
/// `.values_dict(&[])` / `.values_list(&[])` was called with an
/// empty column list. Issue #22. A projection with zero columns
/// would emit `SELECT FROM …` which every dialect rejects as a
/// syntax error; we surface the problem at builder time with a
/// clear message instead.
#[error(
"`.values_dict(...)` / `.values_list(...)` requires at least \
one column. Pass the column names you want in the projection."
)]
EmptyValuesProjection,
}