Skip to main content

qusql_type/
type_insert_replace.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13use alloc::{format, vec::Vec};
14use qusql_parse::{
15    Identifier, InsertReplace, InsertReplaceFlag, InsertReplaceSetPair, InsertReplaceType,
16    OptSpanned, Spanned, issue_todo,
17};
18
19use crate::{
20    BaseType, SelectTypeColumn, Type,
21    schema::lookup_name,
22    type_expression::{ExpressionFlags, type_expression},
23    type_select::{SelectType, type_select, type_select_exprs},
24    typer::{ReferenceType, Typer, did_you_mean, typer_stack},
25};
26
27/// Does the insert yield an auto increment id
28#[derive(Clone, Debug, PartialEq, Eq)]
29pub enum AutoIncrementId {
30    Yes,
31    No,
32    Optional,
33}
34
35pub(crate) fn type_insert_replace<'a>(
36    typer: &mut Typer<'a, '_>,
37    ior: &InsertReplace<'a>,
38) -> (AutoIncrementId, Option<SelectType<'a>>) {
39    let table = &ior.table.identifier;
40    let table_key = typer.qname_to_key(&ior.table);
41    let search_path = typer.search_path();
42    let columns = &ior.columns;
43
44    let (s, auto_increment) =
45        if let Some(schema) = lookup_name(&typer.schemas.schemas, &table_key, search_path) {
46            if schema.view {
47                typer.err("Inserts into views not yet implemented", table);
48            }
49            let mut col_types = Vec::new();
50
51            for col in columns {
52                if let Some(schema_col) = schema.get_column(col.value) {
53                    col_types.push((schema_col.type_.clone(), col.span()));
54                } else {
55                    typer.err("No such column in schema", col);
56                }
57            }
58
59            if let Some(set) = &ior.set {
60                for col in &schema.columns {
61                    if col.auto_increment
62                        || col.default
63                        || !col.type_.not_null
64                        || col.as_.is_some()
65                        || col.generated
66                        || set.pairs.iter().any(|v| v.column == col.identifier)
67                    {
68                        continue;
69                    }
70                    typer.err(
71                        format!(
72                            "No value for column {} provided, but it has no default value",
73                            col.identifier
74                        ),
75                        set,
76                    );
77                }
78            } else {
79                for col in &schema.columns {
80                    if col.auto_increment
81                        || col.default
82                        || !col.type_.not_null
83                        || col.as_.is_some()
84                        || col.generated
85                        || columns.contains(&col.identifier)
86                    {
87                        continue;
88                    }
89                    typer.err(
90                        format!(
91                            "No value for column {} provided, but it has no default value",
92                            col.identifier
93                        ),
94                        &columns.opt_span().unwrap_or(table.span()),
95                    );
96                }
97            }
98
99            (
100                Some(col_types),
101                schema.columns.iter().any(|c| c.auto_increment),
102            )
103        } else {
104            typer.err("Unknown table", table);
105            (None, false)
106        };
107
108    if let Some(values) = &ior.values {
109        for row in &values.1 {
110            for (j, e) in row.iter().enumerate() {
111                if let Some((et, ets)) = s.as_ref().and_then(|v| v.get(j)) {
112                    let t = type_expression(typer, e, ExpressionFlags::default(), et.base());
113                    if typer.matched_type(&t, et).is_none() {
114                        typer
115                            .err(format!("Got type {}", t.t), e)
116                            .frag(format!("Expected {}", et.t), ets);
117                    } else if let Type::Args(_, args) = &t.t {
118                        for (idx, arg_type, _) in args.iter() {
119                            typer.constrain_arg(*idx, arg_type, et);
120                        }
121                    }
122                } else {
123                    type_expression(typer, e, ExpressionFlags::default(), BaseType::Any);
124                }
125            }
126            if let Some(s) = &s
127                && s.len() != row.len()
128            {
129                typer
130                    .err(
131                        format!("Got {} columns", row.len()),
132                        &row.opt_span().unwrap(),
133                    )
134                    .frag(
135                        format!("Expected {}", columns.len()),
136                        &columns.opt_span().unwrap_or(table.span()),
137                    );
138            }
139        }
140    }
141
142    if let Some(select_stmt) = &ior.select
143        && let qusql_parse::Statement::Select(select_inner) = select_stmt
144    {
145        let select = type_select(typer, select_inner, true);
146        if let Some(s) = s {
147            for i in 0..usize::max(s.len(), select.columns.len()) {
148                match (s.get(i), select.columns.get(i)) {
149                    (Some((et, ets)), Some(t)) => {
150                        if typer.matched_type(&t.type_, et).is_none() {
151                            typer
152                                .err(format!("Got type {}", t.type_.t), &t.span)
153                                .frag(format!("Expected {}", et), ets);
154                        }
155                    }
156                    (None, Some(t)) => {
157                        typer.err("Column in select not in insert", &t.span);
158                    }
159                    (Some((_, ets)), None) => {
160                        typer.err("Missing column in select", ets);
161                    }
162                    (None, None) => {
163                        panic!("ICE")
164                    }
165                }
166            }
167        }
168    }
169    // Compound queries (UNION/INTERSECT/EXCEPT) skip the column type check
170
171    let mut guard = typer_stack(
172        typer,
173        |t| core::mem::take(&mut t.reference_types),
174        |t, v| t.reference_types = v,
175    );
176    let typer = &mut guard.typer;
177
178    if let Some(s) = lookup_name(&typer.schemas.schemas, &table_key, search_path) {
179        let mut columns = Vec::new();
180        for c in &s.columns {
181            columns.push((c.identifier.clone(), c.type_.clone()));
182        }
183        for v in &typer.reference_types {
184            if v.name == Some(table.clone()) {
185                typer
186                    .issues
187                    .err("Duplicate definitions", table)
188                    .frag("Already defined here", &v.span);
189            }
190        }
191        typer.reference_types.push(ReferenceType {
192            name: Some(table.clone()),
193            span: table.span(),
194            columns,
195        });
196    }
197
198    if let Some(set) = &ior.set {
199        for InsertReplaceSetPair { column, value, .. } in &set.pairs {
200            let mut cnt = 0;
201            let mut t = None;
202            for r in &typer.reference_types {
203                for c in &r.columns {
204                    if c.0 == *column {
205                        cnt += 1;
206                        t = Some(c.clone());
207                    }
208                }
209            }
210            if cnt > 1 {
211                type_expression(typer, value, ExpressionFlags::default(), BaseType::Any);
212                let mut issue = typer.issues.err("Ambiguous reference", column);
213                for r in &typer.reference_types {
214                    for c in &r.columns {
215                        if c.0 == *column {
216                            issue.frag("Defined here", &r.span);
217                        }
218                    }
219                }
220            } else if let Some(t) = t {
221                let value_type =
222                    type_expression(typer, value, ExpressionFlags::default(), t.1.base());
223                if typer.matched_type(&value_type, &t.1).is_none() {
224                    typer.err(format!("Got type {} expected {}", value_type, t.1), value);
225                } else if let Type::Args(_, args) = &value_type.t {
226                    for (idx, arg_type, _) in args.iter() {
227                        typer.constrain_arg(*idx, arg_type, &t.1);
228                    }
229                }
230            } else {
231                type_expression(typer, value, ExpressionFlags::default(), BaseType::Any);
232                let suggestion = did_you_mean(
233                    column.value,
234                    typer
235                        .reference_types
236                        .iter()
237                        .flat_map(|r| r.columns.iter().map(|(id, _)| id.value)),
238                );
239                let mut issue = typer.err("Unknown identifier", column);
240                if let Some(s) = suggestion {
241                    issue.help(alloc::format!("did you mean `{s}`?"));
242                }
243            }
244        }
245    }
246
247    if let Some(up) = &ior.on_duplicate_key_update {
248        for InsertReplaceSetPair { value, column, .. } in &up.pairs {
249            let mut cnt = 0;
250            let mut t = None;
251            for r in &typer.reference_types {
252                for c in &r.columns {
253                    if c.0 == *column {
254                        cnt += 1;
255                        t = Some(c.clone());
256                    }
257                }
258            }
259            let flags = ExpressionFlags::default().with_in_on_duplicate_key_update(true);
260            if cnt > 1 {
261                type_expression(typer, value, flags, BaseType::Any);
262                let mut issue = typer.issues.err("Ambiguous reference", column);
263                for r in &typer.reference_types {
264                    for c in &r.columns {
265                        if c.0 == *column {
266                            issue.frag("Defined here", &r.span);
267                        }
268                    }
269                }
270            } else if let Some(t) = t {
271                let value_type = type_expression(typer, value, flags, t.1.base());
272                if typer.matched_type(&value_type, &t.1).is_none() {
273                    typer.err(format!("Got type {} expected {}", value_type, t.1), value);
274                } else if let Type::Args(_, args) = &value_type.t {
275                    for (idx, arg_type, _) in args.iter() {
276                        typer.constrain_arg(*idx, arg_type, &t.1);
277                    }
278                }
279            } else {
280                type_expression(typer, value, flags, BaseType::Any);
281                let suggestion = did_you_mean(
282                    column.value,
283                    typer
284                        .reference_types
285                        .iter()
286                        .flat_map(|r| r.columns.iter().map(|(id, _)| id.value)),
287                );
288                let mut issue = typer.err("Unknown identifier", column);
289                if let Some(s) = suggestion {
290                    issue.help(alloc::format!("did you mean `{s}`?"));
291                }
292            }
293        }
294    }
295
296    if let Some(on_conflict) = &ior.on_conflict {
297        match &on_conflict.target {
298            qusql_parse::OnConflictTarget::Columns { names } => {
299                for name in names {
300                    let mut t = None;
301                    for r in &typer.reference_types {
302                        for c in &r.columns {
303                            if c.0 == *name {
304                                t = Some(c.clone());
305                            }
306                        }
307                    }
308                    if t.is_none() {
309                        let suggestion = did_you_mean(
310                            name.value,
311                            typer
312                                .reference_types
313                                .iter()
314                                .flat_map(|r| r.columns.iter().map(|(id, _)| id.value)),
315                        );
316                        let mut issue = typer.err("Unknown identifier", name);
317                        if let Some(s) = suggestion {
318                            issue.help(alloc::format!("did you mean `{s}`?"));
319                        }
320                    }
321                }
322                //TODO check if there is a unique constraint on column
323            }
324            qusql_parse::OnConflictTarget::OnConstraint {
325                on_constraint_span, ..
326            } => {
327                issue_todo!(typer.issues, on_constraint_span);
328            }
329            qusql_parse::OnConflictTarget::None => (),
330        }
331
332        match &on_conflict.action {
333            qusql_parse::OnConflictAction::DoNothing(_) => (),
334            qusql_parse::OnConflictAction::DoUpdateSet {
335                sets,
336                where_,
337                do_update_set_span,
338            } => {
339                let mut excluded_columns = Vec::new();
340                if let Some(schema) = lookup_name(&typer.schemas.schemas, &table_key, search_path)
341                    && !schema.view
342                {
343                    for col in &ior.columns {
344                        if let Some(schema_col) = schema.get_column(col.value) {
345                            excluded_columns
346                                .push((schema_col.identifier.clone(), schema_col.type_.clone()));
347                        }
348                    }
349                }
350
351                typer.reference_types.push(ReferenceType {
352                    name: Some(Identifier::new("EXCLUDED", do_update_set_span.clone())),
353                    span: do_update_set_span.span(),
354                    columns: excluded_columns,
355                });
356
357                for (key, value) in sets {
358                    let mut cnt = 0;
359                    let mut t = None;
360                    for r in &typer.reference_types {
361                        if let Some(name) = &r.name
362                            && name.value == "EXCLUDED"
363                        {
364                            continue;
365                        }
366                        for c in &r.columns {
367                            if c.0 == *key {
368                                cnt += 1;
369                                t = Some(c.clone());
370                            }
371                        }
372                    }
373                    let flags = ExpressionFlags::default().with_in_on_duplicate_key_update(true);
374                    if cnt > 1 {
375                        type_expression(typer, value, flags, BaseType::Any);
376                        let mut issue = typer.issues.err("Ambiguous reference", key);
377                        for r in &typer.reference_types {
378                            for c in &r.columns {
379                                if c.0 == *key {
380                                    issue.frag("Defined here", &r.span);
381                                }
382                            }
383                        }
384                    } else if let Some(t) = t {
385                        let value_type = type_expression(typer, value, flags, t.1.base());
386                        if typer.matched_type(&value_type, &t.1).is_none() {
387                            typer.err(format!("Got type {} expected {}", value_type, t.1), value);
388                        } else if let Type::Args(_, args) = &value_type.t {
389                            for (idx, arg_type, _) in args.iter() {
390                                typer.constrain_arg(*idx, arg_type, &t.1);
391                            }
392                        }
393                    } else {
394                        type_expression(typer, value, flags, BaseType::Any);
395                        let suggestion = did_you_mean(
396                            key.value,
397                            typer
398                                .reference_types
399                                .iter()
400                                .flat_map(|r| r.columns.iter().map(|(id, _)| id.value)),
401                        );
402                        let mut issue = typer.err("Unknown identifier", key);
403                        if let Some(s) = suggestion {
404                            issue.help(alloc::format!("did you mean `{s}`?"));
405                        }
406                    }
407                }
408                if let Some((_, where_)) = where_ {
409                    type_expression(typer, where_, ExpressionFlags::default(), BaseType::Bool);
410                }
411            }
412        }
413    }
414
415    let returning_select = match &ior.returning {
416        Some((returning_span, returning_exprs)) => {
417            let columns = type_select_exprs(typer, returning_exprs, true)
418                .into_iter()
419                .map(|(name, type_, span)| SelectTypeColumn { name, type_, span })
420                .collect();
421            Some(SelectType {
422                columns,
423                select_span: returning_span.join_span(returning_exprs),
424            })
425        }
426        None => None,
427    };
428
429    core::mem::drop(guard);
430
431    let auto_increment_id = if auto_increment && matches!(ior.type_, InsertReplaceType::Insert(_)) {
432        if ior
433            .flags
434            .iter()
435            .any(|f| matches!(f, InsertReplaceFlag::Ignore(_)))
436            || ior.on_duplicate_key_update.is_some()
437        {
438            AutoIncrementId::Optional
439        } else {
440            AutoIncrementId::Yes
441        }
442    } else {
443        AutoIncrementId::No
444    };
445
446    (auto_increment_id, returning_select)
447}