cdbc_mssql/
arguments.rs

1use cdbc::arguments::Arguments;
2use cdbc::encode::Encode;
3use crate::database::Mssql;
4use crate::io::MssqlBufMutExt;
5use crate::protocol::rpc::StatusFlags;
6use cdbc::types::Type;
7
8#[derive(Default)]
9pub struct MssqlArguments {
10    // next ordinal to be used when formatting a positional parameter name
11    pub ordinal: usize,
12    // temporary string buffer used to format parameter names
13    name: String,
14    pub data: Vec<u8>,
15    pub declarations: String,
16}
17
18impl MssqlArguments {
19    pub fn add_named<'q, T: Encode<'q, Mssql> + Type<Mssql>>(
20        &mut self,
21        name: &str,
22        value: T,
23    ) {
24        let ty = value.produces().unwrap_or_else(T::type_info);
25
26        let mut ty_name = String::new();
27        ty.0.fmt(&mut ty_name);
28
29        self.data.put_b_varchar(name); // [ParamName]
30        self.data.push(0); // [StatusFlags]
31
32        ty.0.put(&mut self.data); // [TYPE_INFO]
33        ty.0.put_value(&mut self.data, value); // [ParamLenData]
34    }
35
36    pub fn add_unnamed<'q, T: Encode<'q, Mssql> + Type<Mssql>>(&mut self, value: T) {
37        self.add_named("", value);
38    }
39
40    pub fn declare<'q, T: Encode<'q, Mssql> + Type<Mssql>>(
41        &mut self,
42        name: &str,
43        initial_value: T,
44    ) {
45        let ty = initial_value.produces().unwrap_or_else(T::type_info);
46
47        let mut ty_name = String::new();
48        ty.0.fmt(&mut ty_name);
49
50        self.data.put_b_varchar(name); // [ParamName]
51        self.data.push(StatusFlags::BY_REF_VALUE.bits()); // [StatusFlags]
52
53        ty.0.put(&mut self.data); // [TYPE_INFO]
54        ty.0.put_value(&mut self.data, initial_value); // [ParamLenData]
55    }
56
57    pub fn append(&mut self, arguments: &mut MssqlArguments) {
58        self.ordinal += arguments.ordinal;
59        self.data.append(&mut arguments.data);
60    }
61
62    pub fn add<'q, T>(&mut self, value: T)
63    where
64        T: Encode<'q, Mssql> + Type<Mssql>,
65    {
66        let ty = value.produces().unwrap_or_else(T::type_info);
67
68        // produce an ordinal parameter name
69        //  @p1, @p2, ... @pN
70
71        self.name.clear();
72        self.name.push_str("@p");
73
74        self.ordinal += 1;
75        self.name.push_str(itoa::Buffer::new().format(self.ordinal));
76
77        let MssqlArguments {
78            ref name,
79            ref mut declarations,
80            ref mut data,
81            ..
82        } = self;
83
84        // add this to our variable declaration list
85        //  @p1 int, @p2 nvarchar(10), ...
86
87        if !declarations.is_empty() {
88            declarations.push_str(",");
89        }
90
91        declarations.push_str(name);
92        declarations.push(' ');
93        ty.0.fmt(declarations);
94
95        // write out the parameter
96
97        data.put_b_varchar(name); // [ParamName]
98        data.push(0); // [StatusFlags]
99
100        ty.0.put(data); // [TYPE_INFO]
101        ty.0.put_value(data, value); // [ParamLenData]
102    }
103}
104
105impl<'q> Arguments<'q> for MssqlArguments {
106    type Database = Mssql;
107
108    fn reserve(&mut self, _additional: usize, size: usize) {
109        self.data.reserve(size + 10); // est. 4 chars for name, 1 for status, 1 for TYPE_INFO
110    }
111
112    fn add<T>(&mut self, value: T)
113    where
114        T: 'q + Encode<'q, Self::Database> + Type<Mssql>,
115    {
116        self.add(value)
117    }
118}