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 3855 : pub(super) fn visit(state: &mut AnalysisState, morsel: Range<usize>) {
16 84087 : for node_id in morsel {
17 84087 : let node = &state.ast[node_id];
18 84087 : if node.node_type() != NodeType::OBJECT_SQL_PARAMETER_REF {
19 83151 : continue;
20 936 : }
21 936 : let parent = &state.ast[node.parent() as usize];
22 936 : let kind = if node.attribute_key() == K::SQL_TABLEREF_TABLE && parent.node_type() == NodeType::OBJECT_SQL_TABLEREF {
23 717 : ParameterKind::Table
24 : } else {
25 219 : ParameterKind::Scalar
26 : };
27 936 : let [name] = attrs!(state.children(node), [K::SQL_PARAMETER_NAME]);
28 936 : let name = name.and_then(|n| state.read_name_path(n).last().map(|&id| state.name(id).text)).map(unquote);
29 936 : let cast_type = match kind {
30 219 : ParameterKind::Scalar => cast_type(state, node, parent),
31 717 : ParameterKind::Table => None,
32 : };
33 936 : let node_id = node_id as u32;
34 936 : let parameter = Parameter {
35 936 : ast_node_id: node_id,
36 936 : ast_statement_id: state.find_statement_id(node_id),
37 936 : location: *node.symbol_span(),
38 936 : kind,
39 936 : name,
40 936 : cast_type,
41 936 : };
42 936 : state.out.parameters.push(parameter);
43 : }
44 3855 : }
45 :
46 : /// The type of the cast whose value is the parameter
47 219 : fn cast_type(state: &AnalysisState, node: &Node, parent: &Node) -> Option<SqlType> {
48 219 : 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 177 : _ => 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 219 : }
56 :
57 : /// The parser keeps the quotes of names from string literals (`:'mode'`, see docs/design/core_quirks.md)
58 926 : fn unquote(name: Arc<str>) -> Arc<str> {
59 926 : match name.strip_prefix('\'').and_then(|n| n.strip_suffix('\'')) {
60 1 : Some(inner) => inner.into(),
61 925 : None => name,
62 : }
63 926 : }
|