Skip to main content

keelson_mysql/statement/
values.rs

1use keelson_core::clause::{HasLimit, HasOrderBy, HasValues, Limit, OrderBy, Values};
2use keelson_core::expr::{Expr, IntoExpr, IntoExprList};
3use keelson_core::{Dialect, Error, Expression, Mod, Query, QueryExtensions, QueryType, SqlWriter};
4
5use crate::Mysql;
6
7/// The MySQL `VALUES` statement (MySQL 8.0.19+).
8///
9/// From <https://dev.mysql.com/doc/refman/8.4/en/values.html>:
10///
11/// ```text
12/// VALUES row_constructor_list [ORDER BY column_designator] [LIMIT number]
13///
14/// row_constructor_list: ROW(value_list) [, ROW(value_list)] ...
15/// ```
16///
17/// Unlike an `INSERT`'s `VALUES` list, every row here is spelled with the `ROW`
18/// keyword — that is the grammar, not a flourish — and the result's columns are
19/// named `column_0`, `column_1`, …, which is what an `ORDER BY` refers to.
20///
21/// The rows are the shared [`Values`] clause, whose `VALUES (…), (…)` rendering
22/// is the `INSERT` spelling; this statement writes the `ROW` form itself. The
23/// clause's query alternative belongs to `INSERT` and is a recorded build error
24/// here.
25#[derive(Debug, Clone, Default)]
26pub struct ValuesQuery {
27    /// The rows.
28    pub values: Values,
29    /// `ORDER BY column_designator`.
30    pub order_by: OrderBy,
31    /// `LIMIT number`.
32    pub limit: Limit,
33}
34
35impl ValuesQuery {
36    /// A `VALUES` with no rows yet — which does not build until it has one.
37    pub fn new() -> ValuesQuery {
38        ValuesQuery::default()
39    }
40
41    /// Apply more mods to an existing query.
42    pub fn apply(&mut self, mods: impl Mod<ValuesQuery>) {
43        mods.apply(self);
44    }
45}
46
47impl Expression for ValuesQuery {
48    fn write_sql(&self, w: &mut SqlWriter<'_>) {
49        // The query alternative of the shared clause belongs to INSERT; a
50        // standalone VALUES has no slot for it.
51        if self.values.query.is_some() {
52            w.record_error(Error::other(
53                "a standalone VALUES statement takes rows; a source query belongs to INSERT",
54            ));
55            return;
56        }
57        if self.values.rows.is_empty() {
58            w.record_error(Error::Incomplete("the rows of a VALUES statement"));
59            return;
60        }
61
62        w.push_str("VALUES ");
63        for (i, row) in self.values.rows.iter().enumerate() {
64            if i > 0 {
65                w.push_str(", ");
66            }
67            // `ROW` welded to the row's own parentheses: `ROW(?, ?)`, the
68            // manual's spelling.
69            w.push_str("ROW");
70            w.write_expr(row);
71        }
72
73        w.write_if(!self.order_by.is_empty(), " ", &self.order_by, "");
74        w.write_if(!self.limit.is_empty(), " ", &self.limit, "");
75    }
76}
77
78impl Query for ValuesQuery {
79    fn query_type(&self) -> QueryType {
80        // Rows come back, exactly as from a SELECT.
81        QueryType::Select
82    }
83
84    fn dialect(&self) -> &dyn Dialect {
85        &Mysql
86    }
87}
88
89impl<H, L, M> QueryExtensions<H, L, M> for ValuesQuery {}
90
91impl IntoExpr for ValuesQuery {
92    fn into_expr(self) -> Expr {
93        crate::query(self)
94    }
95}
96
97impl IntoExprList for ValuesQuery {
98    fn into_expr_list(self) -> Vec<Expr> {
99        vec![self.into_expr()]
100    }
101}
102
103impl HasValues for ValuesQuery {
104    fn values_mut(&mut self) -> &mut Values {
105        &mut self.values
106    }
107}
108
109impl HasOrderBy for ValuesQuery {
110    fn order_by_mut(&mut self) -> &mut OrderBy {
111        &mut self.order_by
112    }
113}
114
115impl HasLimit for ValuesQuery {
116    fn limit_mut(&mut self) -> &mut Limit {
117        &mut self.limit
118    }
119}