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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! The statement types, each shaped by MySQL's own grammar: the five DML ones,
//! and the `VALUES` and `TABLE` statements of MySQL 8.0.19+.
//!
//! Every one composes the shared clause structs as named fields, in the order
//! *13.2 Data Manipulation Statements* lists them, and implements the `Has*`
//! traits for the clauses it actually has. *Not* implementing one is how "this
//! statement has no such clause" is said: `select::having(..)` will not compile
//! against an `UpdateQuery`.
//!
//! # What MySQL does not have, and therefore is not here
//!
//! * **No `RETURNING`** on any of them. There is no
//! [`Returning`](keelson_core::clause::Returning) field anywhere in this crate
//! and no `returning` mod to apply to one.
//! * **No `FETCH`**, so no [`HasFetch`](keelson_core::clause::HasFetch).
//! * **No `WITH` on `INSERT` or `REPLACE`.** MySQL permits a `WITH` clause "at the
//! beginning of `SELECT`, `UPDATE`, and `DELETE` statements" and, for
//! `INSERT … SELECT`, only *immediately preceding the `SELECT`*
//! (*15.2.20 WITH*). So `WITH c AS (…) INSERT …` is not MySQL; the CTE goes
//! inside the sub-query handed to [`insert::query`](crate::insert::query).
//! * **No `OFFSET` on `UPDATE` or `DELETE`** — their `LIMIT` takes a row count and
//! nothing else.
//!
//! # Which table a mod means
//!
//! | statement | [`HasTableRef`](keelson_core::clause::HasTableRef) | [`HasTargetTable`] | [`HasExtraTables`] | [`HasDeleteTables`] |
//! |---|---|---|---|---|
//! | `SELECT` | first `FROM` item | — | further `FROM` items | — |
//! | `INSERT`/`REPLACE` | `INTO` target | — | — | — |
//! | `UPDATE` | — | the updated `table_references` | further ones | — |
//! | `DELETE` | first `USING` item | — | further `USING` items | the `FROM` list |
//!
//! `UPDATE` is the row that differs from PostgreSQL: MySQL has no `UPDATE … FROM`,
//! so there is only one table list and it *is* the target — joins and all. That is
//! why `UpdateQuery` implements [`HasTargetTable`] and not `HasTableRef`, and why
//! `update::inner_join` lands on the updated table rather than on a separate
//! from-item.
pub use DeleteQuery;
pub use InsertQuery;
pub use ReplaceQuery;
pub use SelectQuery;
pub use TableQuery;
pub use UpdateQuery;
pub use ValuesQuery;
use Cow;
use TableRef;
/// A statement whose table list is the thing being modified: the
/// `table_references` an `UPDATE` writes to.
///
/// `SELECT`, `INSERT` and `REPLACE` have one table each and use
/// [`HasTableRef`](keelson_core::clause::HasTableRef) for it, which is why
/// `update::table(..)` cannot be applied to them.
/// A statement whose table list may hold more than one entry.
///
/// MySQL's `table_references` is comma-separated, and a comma there means the same
/// thing as `CROSS JOIN`. The first entry lives in `HasTableRef` or
/// [`HasTargetTable`]; the rest are appended here.
/// A `DELETE`'s `FROM` list — the tables rows are actually removed from.
///
/// Separate from every other table trait because `DELETE` is the one statement
/// where the tables being modified and the tables being *read* are two different
/// lists:
///
/// ```text
/// DELETE FROM t1, t2 USING t1 INNER JOIN t2 ON … WHERE …
/// ```
///
/// The partition list comes with it, because `DELETE` is also the one statement
/// that writes `PARTITION` *after* the alias (*15.2.2*):
/// `DELETE FROM tbl [[AS] alias] [PARTITION (…)]`. Everywhere else it precedes
/// the alias, which is where [`TableRef`] puts it — so the chain's partitions are
/// moved out of the table reference and into this slot.
/// Write a keyword and its comma-separated table list, skipping absent entries.
///
/// An entry with no table renders nothing, so it must not contribute a comma
/// either; and if the leading item is absent the whole clause goes, because
/// `FROM , \`x\`` is not a repair of anything.
///
/// Two things must not go with it: joins and the extra items. Joins hang off
/// the leading item, so with no item they have nowhere to attach; extra items
/// are second and later entries of a list the leading item opens, so with no
/// item there is no list to be in. Dropping either one the caller asked for
/// would build *valid* SQL that silently means something else, which no
/// grammar or engine can catch after the fact. That is recorded as
/// [`Error::Incomplete`](keelson_core::Error::Incomplete) with `missing`
/// naming the absent item. (`UPDATE` reaches neither guard: its absent target
/// is already an `Incomplete` before this writer runs, so its `table_also`
/// entries always have their leading `table_references` entry.)
/// Write a statement's optimizer hints and modifiers, each followed by a space.
///
/// *10.9.2* puts the hint comment immediately after the statement's first
/// keyword, before the modifiers: `SELECT /*+ … */ DISTINCT …`.