sql_type/
typer.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::borrow::Cow;
14
15use crate::{
16    schema::{Schema, Schemas},
17    type_::{ArgType, BaseType, FullType},
18    ArgumentKey, Type, TypeOptions,
19};
20use alloc::sync::Arc;
21use alloc::vec::Vec;
22use alloc::{collections::BTreeMap, format};
23use sql_parse::{
24    Identifier, IssueHandle, Issues, OptSpanned, QualifiedName, SQLDialect, Span, Spanned,
25};
26
27#[derive(Clone, Debug)]
28pub(crate) struct ReferenceType<'a> {
29    pub(crate) name: Option<Identifier<'a>>,
30    pub(crate) span: Span,
31    pub(crate) columns: Vec<(Identifier<'a>, FullType<'a>)>,
32}
33
34pub(crate) struct Typer<'a, 'b> {
35    pub(crate) issues: &'b mut Issues<'a>,
36    pub(crate) schemas: &'b Schemas<'a>,
37    pub(crate) with_schemas: BTreeMap<&'a str, &'b Schema<'a>>,
38    pub(crate) reference_types: Vec<ReferenceType<'a>>,
39    pub(crate) arg_types: Vec<(ArgumentKey<'a>, FullType<'a>)>,
40    pub(crate) options: &'b TypeOptions,
41}
42
43impl<'a, 'b> Typer<'a, 'b> {
44    pub(crate) fn with_schemas<'c>(
45        &'c mut self,
46        schemas: BTreeMap<&'a str, &'c Schema<'a>>,
47    ) -> Typer<'a, 'c>
48    where
49        'b: 'c,
50    {
51        Typer::<'a, 'c> {
52            issues: self.issues,
53            schemas: self.schemas,
54            with_schemas: schemas,
55            reference_types: self.reference_types.clone(),
56            arg_types: self.arg_types.clone(),
57            options: self.options,
58        }
59    }
60
61    pub(crate) fn dialect(&self) -> SQLDialect {
62        self.options.parse_options.get_dialect()
63    }
64
65    pub(crate) fn constrain_arg(&mut self, idx: usize, arg_type: &ArgType, t: &FullType<'a>) {
66        // TODO Use arg_type
67        let ot = match self
68            .arg_types
69            .iter_mut()
70            .find(|(k, _)| k == &ArgumentKey::Index(idx))
71        {
72            Some((_, v)) => v,
73            None => {
74                self.arg_types
75                    .push((ArgumentKey::Index(idx), FullType::new(BaseType::Any, false)));
76                &mut self.arg_types.last_mut().unwrap().1
77            }
78        };
79        if t.base() != BaseType::Any || ot.base() == BaseType::Any {
80            *ot = t.clone();
81        }
82        if matches!(arg_type, ArgType::ListHack) {
83            ot.list_hack = true;
84        }
85    }
86
87    pub(crate) fn matched_type(&mut self, t1: &Type<'a>, t2: &Type<'a>) -> Option<Type<'a>> {
88        if t1 == &Type::Invalid && t2 == &Type::Invalid {
89            return Some(t1.clone());
90        }
91        if t1 == &Type::Null {
92            return Some(t2.clone());
93        }
94        if t2 == &Type::Null {
95            return Some(t1.clone());
96        }
97
98        let mut t1b = t1.base();
99        let mut t2b = t2.base();
100        if t1b == BaseType::Any {
101            t1b = t2b;
102        }
103        if t2b == BaseType::Any {
104            t2b = t1b;
105        }
106        if t1b != t2b {
107            return None;
108        }
109
110        for t in &[t1, t2] {
111            if let Type::Args(_, a) = t {
112                for (idx, arg_type, _) in a.iter() {
113                    self.constrain_arg(*idx, arg_type, &FullType::new(t1b, false));
114                }
115            }
116        }
117        if t1b == BaseType::Any {
118            let mut args = Vec::new();
119            for t in &[t1, t2] {
120                if let Type::Args(_, a) = t {
121                    args.extend_from_slice(a);
122                }
123            }
124            if !args.is_empty() {
125                return Some(Type::Args(t1b, Arc::new(args)));
126            }
127        }
128        Some(t1b.into())
129    }
130
131    pub(crate) fn ensure_type(
132        &mut self,
133        span: &impl Spanned,
134        given: &FullType<'a>,
135        expected: &FullType<'a>,
136    ) {
137        if self.matched_type(given, expected).is_none() {
138            self.issues.err(
139                format!("Expected type {} got {}", expected.t, given.t),
140                span,
141            );
142        }
143    }
144
145    pub(crate) fn ensure_base(
146        &mut self,
147        span: &impl Spanned,
148        given: &FullType<'a>,
149        expected: BaseType,
150    ) {
151        self.ensure_type(span, given, &FullType::new(expected, false));
152    }
153
154    pub(crate) fn get_schema(&self, name: &str) -> Option<&'b Schema<'a>> {
155        if let Some(schema) = self.with_schemas.get(name) {
156            Some(schema)
157        } else {
158            self.schemas.schemas.get(name)
159        }
160    }
161
162    pub(crate) fn err(
163        &mut self,
164        message: impl Into<Cow<'static, str>>,
165        span: &impl Spanned,
166    ) -> IssueHandle<'a, '_> {
167        self.issues.err(message, span)
168    }
169
170    pub(crate) fn warn(
171        &mut self,
172        message: impl Into<Cow<'static, str>>,
173        span: &impl Spanned,
174    ) -> IssueHandle<'a, '_> {
175        self.issues.warn(message, span)
176    }
177}
178
179pub(crate) struct TyperStack<'a, 'b, 'c, V, D: FnOnce(&mut Typer<'a, 'b>, V)> {
180    pub(crate) typer: &'c mut Typer<'a, 'b>,
181    value_drop: Option<(V, D)>,
182}
183
184impl<'a, 'b, 'c, V, D: FnOnce(&mut Typer<'a, 'b>, V)> Drop for TyperStack<'a, 'b, 'c, V, D> {
185    fn drop(&mut self) {
186        if let Some((v, d)) = self.value_drop.take() {
187            (d)(self.typer, v)
188        }
189    }
190}
191
192pub(crate) fn typer_stack<
193    'a,
194    'b,
195    'c,
196    V,
197    C: FnOnce(&mut Typer<'a, 'b>) -> V,
198    D: FnOnce(&mut Typer<'a, 'b>, V),
199>(
200    typer: &'c mut Typer<'a, 'b>,
201    c: C,
202    d: D,
203) -> TyperStack<'a, 'b, 'c, V, D> {
204    let v = c(typer);
205    TyperStack {
206        typer,
207        value_drop: Some((v, d)),
208    }
209}
210
211pub(crate) fn unqualified_name<'b, 'c>(
212    issues: &mut Issues<'_>,
213    name: &'c QualifiedName<'b>,
214) -> &'c Identifier<'b> {
215    if !name.prefix.is_empty() {
216        issues.err(
217            "Expected unqualified name",
218            &name.prefix.opt_span().unwrap(),
219        );
220    }
221    &name.identifier
222}