Line data Source code
1 : //! Collect the parameters of a script.
2 : //!
3 : //! A parameter stands for a table when it is the table of a table reference (`from :source`,
4 : //! `insert into :target`), and for a scalar value everywhere else (`where x > :min`). A scalar parameter that is
5 : //! the value of a cast (`:min::int`, `cast(:day as date)`) has the type of the cast.
6 :
7 : use super::sql_types::{read_sql_type, SqlType};
8 : use super::state::AnalysisState;
9 : use super::types::*;
10 : use dashql_buffers::parser::{AttributeKey as K, Node, NodeType};
11 : use dashql_parser::attrs;
12 : use std::ops::Range;
13 : use std::sync::Arc;
14 :
15 3589 : pub(super) fn visit(state: &mut AnalysisState, morsel: Range<usize>) {
16 78912 : for node_id in morsel {
17 78912 : let node = &state.ast[node_id];
18 78912 : if node.node_type() != NodeType::OBJECT_SQL_PARAMETER_REF {
19 78028 : continue;
20 884 : }
21 884 : let parent = &state.ast[node.parent() as usize];
22 884 : let kind = if node.attribute_key() == K::SQL_TABLEREF_TABLE && parent.node_type() == NodeType::OBJECT_SQL_TABLEREF {
23 694 : ParameterKind::Table
24 : } else {
25 190 : ParameterKind::Scalar
26 : };
27 884 : let [name] = attrs!(state.children(node), [K::SQL_PARAMETER_NAME]);
28 884 : let name = name.and_then(|n| state.read_name_path(n).last().map(|&id| state.name(id).text)).map(unquote);
29 884 : let cast_type = match kind {
30 190 : ParameterKind::Scalar => cast_type(state, node, parent),
31 694 : ParameterKind::Table => None,
32 : };
33 884 : let node_id = node_id as u32;
34 884 : let parameter = Parameter {
35 884 : ast_node_id: node_id,
36 884 : ast_statement_id: state.find_statement_id(node_id),
37 884 : location: *node.symbol_span(),
38 884 : kind,
39 884 : name,
40 884 : cast_type,
41 884 : };
42 884 : state.out.parameters.push(parameter);
43 : }
44 3589 : }
45 :
46 : /// The type of the cast whose value is the parameter
47 190 : fn cast_type(state: &AnalysisState, node: &Node, parent: &Node) -> Option<SqlType> {
48 190 : let type_key = match (parent.node_type(), node.attribute_key()) {
49 33 : (NodeType::OBJECT_SQL_TYPECAST_EXPRESSION, K::SQL_TYPECAST_VALUE) => K::SQL_TYPECAST_TYPE,
50 9 : (NodeType::OBJECT_SQL_FUNCTION_CAST_ARGS, K::SQL_FUNCTION_CAST_VALUE) => K::SQL_FUNCTION_CAST_TYPE,
51 148 : _ => return None,
52 : };
53 84 : let type_node = state.children(parent).iter().find(|c| c.attribute_key() == type_key)?;
54 42 : read_sql_type(state.parsed, type_node)
55 190 : }
56 :
57 : /// The parser keeps the quotes of names from string literals (`:'mode'`, see docs/design/core_quirks.md)
58 874 : fn unquote(name: Arc<str>) -> Arc<str> {
59 874 : match name.strip_prefix('\'').and_then(|n| n.strip_suffix('\'')) {
60 1 : Some(inner) => inner.into(),
61 873 : None => name,
62 : }
63 874 : }
|