google_cloud_spanner/
mutation.rs

1use prost_types::{ListValue, Value};
2
3use google_cloud_googleapis::spanner::v1::mutation::{Delete, Operation, Write};
4use google_cloud_googleapis::spanner::v1::Mutation;
5
6use crate::key::KeySet;
7use crate::statement::{ToKind, ToStruct};
8
9fn write(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Write {
10    let values = values
11        .iter()
12        .map(|x| Value {
13            kind: Some(x.to_kind()),
14        })
15        .collect();
16
17    Write {
18        table: table.to_string(),
19        columns: columns.iter().map(|x| x.to_string()).collect(),
20        values: vec![ListValue { values }],
21    }
22}
23
24fn write_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Write {
25    let mut columns = Vec::with_capacity(columns_ans_values.len());
26    let mut values = Vec::with_capacity(columns_ans_values.len());
27    columns_ans_values.iter().for_each(|x| {
28        columns.push(x.0.to_string());
29        values.push(Value {
30            kind: Some(x.1.to_kind()),
31        })
32    });
33    Write {
34        table: table.to_string(),
35        columns,
36        values: vec![ListValue { values }],
37    }
38}
39
40fn write_struct(table: &str, to_struct: impl ToStruct) -> Write {
41    let kinds = to_struct.to_kinds();
42    let mut columns = Vec::with_capacity(kinds.len());
43    let mut values = Vec::with_capacity(kinds.len());
44    kinds.into_iter().for_each(|x| {
45        columns.push(x.0.to_string());
46        values.push(Value { kind: Some(x.1) })
47    });
48    Write {
49        table: table.to_string(),
50        columns,
51        values: vec![ListValue { values }],
52    }
53}
54
55/// Insert returns a Mutation to insert a row into a table. If the row already
56/// exists, the write or transaction fails with codes.AlreadyExists.
57pub fn insert(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
58    Mutation {
59        operation: Some(Operation::Insert(write(table, columns, values))),
60    }
61}
62
63/// insert_map returns a Mutation to insert a row into a table, specified by
64/// a map of column name to value. If the row already exists, the write or
65/// transaction fails with codes.AlreadyExists.
66pub fn insert_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
67    Mutation {
68        operation: Some(Operation::Insert(write_map(table, columns_ans_values))),
69    }
70}
71
72/// insert_struct returns a Mutation to insert a row into a table, specified by
73/// a Rust struct.  If the row already exists, the write or transaction fails with
74/// codes.AlreadyExists.
75pub fn insert_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
76    Mutation {
77        operation: Some(Operation::Insert(write_struct(table, to_struct))),
78    }
79}
80
81/// update returns a Mutation to update a row in a table. If the row does not
82/// already exist, the write or transaction fails.
83pub fn update(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
84    Mutation {
85        operation: Some(Operation::Update(write(table, columns, values))),
86    }
87}
88
89/// update_map returns a Mutation to update a row in a table, specified by
90/// a map of column to value. If the row does not already exist, the write or
91/// transaction fails.
92pub fn update_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
93    Mutation {
94        operation: Some(Operation::Update(write_map(table, columns_ans_values))),
95    }
96}
97
98/// update_struct returns a Mutation to update a row in a table, specified by a Go
99/// struct. If the row does not already exist, the write or transaction fails.
100pub fn update_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
101    Mutation {
102        operation: Some(Operation::Update(write_struct(table, to_struct))),
103    }
104}
105
106/// replace returns a Mutation to insert a row into a table, deleting any
107/// existing row. Unlike InsertOrUpdate, this means any values not explicitly
108/// written become NULL.
109///
110/// For a similar example, See Update.
111pub fn replace(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
112    Mutation {
113        operation: Some(Operation::Replace(write(table, columns, values))),
114    }
115}
116
117/// replace_map returns a Mutation to insert a row into a table, deleting any
118/// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
119/// written become NULL.  The row is specified by a map of column to value.
120///
121/// For a similar example, See update_map.
122pub fn replace_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
123    Mutation {
124        operation: Some(Operation::Replace(write_map(table, columns_ans_values))),
125    }
126}
127
128/// replace_struct returns a Mutation to insert a row into a table, deleting any existing row.
129///
130/// For a similar example, See update_struct.
131pub fn replace_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
132    Mutation {
133        operation: Some(Operation::Replace(write_struct(table, to_struct))),
134    }
135}
136
137/// insert_or_update returns a Mutation to insert a row into a table. If the row
138/// already exists, it updates it instead. Any column values not explicitly
139/// written are preserved.
140///
141/// For a similar example, See update.
142pub fn insert_or_update(table: &str, columns: &[&str], values: &[&dyn ToKind]) -> Mutation {
143    Mutation {
144        operation: Some(Operation::InsertOrUpdate(write(table, columns, values))),
145    }
146}
147
148/// insert_or_update returns a Mutation to insert a row into a table. If the row
149/// already exists, it updates it instead. Any column values not explicitly
150/// written are preserved.
151///
152/// For a similar example, See update.
153pub fn insert_or_update_map(table: &str, columns_ans_values: &[(&str, &dyn ToKind)]) -> Mutation {
154    Mutation {
155        operation: Some(Operation::InsertOrUpdate(write_map(table, columns_ans_values))),
156    }
157}
158
159/// insert_or_update_struct returns a Mutation to insert a row into a table,
160/// specified by a Go struct. If the row already exists, it updates it instead.
161/// Any column values not explicitly written are preserved.
162/// For a similar example, See update_struct.
163pub fn insert_or_update_struct(table: &str, to_struct: impl ToStruct) -> Mutation {
164    Mutation {
165        operation: Some(Operation::InsertOrUpdate(write_struct(table, to_struct))),
166    }
167}
168
169/// delete removes the rows described by the KeySet from the table. It succeeds
170/// whether or not the keys were present.
171pub fn delete(table: &str, key_set: impl Into<KeySet>) -> Mutation {
172    Mutation {
173        operation: Some(Operation::Delete(Delete {
174            table: table.to_string(),
175            key_set: Some(key_set.into().inner),
176        })),
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use prost_types::value::Kind;
183
184    use google_cloud_googleapis::spanner::*;
185
186    use crate::key::*;
187    use crate::mutation::*;
188    use crate::statement::{Kinds, ToKind, Types};
189    use crate::value::CommitTimestamp;
190
191    struct TestStruct {
192        pub struct_field: String,
193    }
194
195    impl ToStruct for TestStruct {
196        fn to_kinds(&self) -> Kinds {
197            vec![("StructField", self.struct_field.to_kind())]
198        }
199
200        fn get_types() -> Types {
201            vec![("StructField", String::get_type())]
202        }
203    }
204
205    #[test]
206    fn test_insert() {
207        let mutation = insert(
208            "Guild",
209            &["GuildId", "UserId", "UpdatedAt"],
210            &[&"1", &"2", &CommitTimestamp::new()],
211        );
212        match mutation.operation.unwrap() {
213            v1::mutation::Operation::Insert(mut w) => {
214                assert_eq!("Guild", w.table);
215                assert_eq!(3, w.values.pop().unwrap().values.len());
216                assert_eq!("UpdatedAt", w.columns.pop().unwrap());
217                assert_eq!("UserId", w.columns.pop().unwrap());
218                assert_eq!("GuildId", w.columns.pop().unwrap());
219            }
220            _ => panic!("invalid operation"),
221        }
222    }
223
224    #[test]
225    fn test_insert_map() {
226        let user_id = 1;
227        let mutation = insert_map(
228            "Guild",
229            &[
230                ("UserId", &"aa"),
231                ("GuildId", &user_id),
232                ("updatedAt", &CommitTimestamp::new()),
233            ],
234        );
235        match mutation.operation.unwrap() {
236            v1::mutation::Operation::Insert(mut w) => {
237                assert_eq!("Guild", w.table);
238                assert_eq!(3, w.values.pop().unwrap().values.len());
239                assert_eq!(3, w.columns.len());
240            }
241            _ => panic!("invalid operation"),
242        }
243    }
244
245    #[test]
246    fn test_insert_struct() {
247        let mutation = insert_struct(
248            "Guild",
249            TestStruct {
250                struct_field: "abc".to_string(),
251            },
252        );
253        match mutation.operation.unwrap() {
254            v1::mutation::Operation::Insert(w) => assert_struct(w),
255            _ => panic!("invalid operation"),
256        }
257    }
258
259    #[test]
260    fn test_insert_struct_ref() {
261        let mutation = insert_struct(
262            "Guild",
263            TestStruct {
264                struct_field: "abc".to_string(),
265            },
266        );
267        match mutation.operation.unwrap() {
268            v1::mutation::Operation::Insert(w) => assert_struct(w),
269            _ => panic!("invalid operation"),
270        }
271    }
272
273    #[test]
274    fn test_update() {
275        let mutation = update(
276            "Guild",
277            &["GuildId", "UserId", "UpdatedAt"],
278            &[&"1", &"2", &CommitTimestamp::new()],
279        );
280        match mutation.operation.unwrap() {
281            v1::mutation::Operation::Update(mut w) => {
282                assert_eq!("Guild", w.table);
283                assert_eq!(3, w.values.pop().unwrap().values.len());
284                assert_eq!(3, w.columns.len());
285            }
286            _ => panic!("invalid operation"),
287        }
288    }
289
290    #[test]
291    fn test_update_struct() {
292        let mutation = update_struct(
293            "Guild",
294            TestStruct {
295                struct_field: "abc".to_string(),
296            },
297        );
298        match mutation.operation.unwrap() {
299            v1::mutation::Operation::Update(w) => assert_struct(w),
300            _ => panic!("invalid operation"),
301        }
302    }
303
304    #[test]
305    fn test_update_struct_ref() {
306        let st = TestStruct {
307            struct_field: "abc".to_string(),
308        };
309        let mutation = update_struct("Guild", st);
310        match mutation.operation.unwrap() {
311            v1::mutation::Operation::Update(w) => assert_struct(w),
312            _ => panic!("invalid operation"),
313        }
314    }
315
316    #[test]
317    fn test_replace() {
318        let mutation = replace(
319            "Guild",
320            &["GuildId", "UserId", "UpdatedAt"],
321            &[&"1", &"2", &CommitTimestamp::new()],
322        );
323        match mutation.operation.unwrap() {
324            v1::mutation::Operation::Replace(mut w) => {
325                assert_eq!("Guild", w.table);
326                assert_eq!(3, w.values.pop().unwrap().values.len());
327                assert_eq!(3, w.columns.len());
328            }
329            _ => panic!("invalid operation"),
330        }
331    }
332
333    #[test]
334    fn test_replace_struct() {
335        let mutation = replace_struct(
336            "Guild",
337            TestStruct {
338                struct_field: "abc".to_string(),
339            },
340        );
341        match mutation.operation.unwrap() {
342            v1::mutation::Operation::Replace(w) => assert_struct(w),
343            _ => panic!("invalid operation"),
344        }
345    }
346
347    #[test]
348    fn test_insert_or_update() {
349        let mutation = insert_or_update(
350            "Guild",
351            &["GuildId", "UserId", "UpdatedAt"],
352            &[&"1", &"2", &CommitTimestamp::new()],
353        );
354        match mutation.operation.unwrap() {
355            v1::mutation::Operation::InsertOrUpdate(mut w) => {
356                assert_eq!("Guild", w.table);
357                assert_eq!(3, w.values.pop().unwrap().values.len());
358                assert_eq!(3, w.columns.len());
359            }
360            _ => panic!("invalid operation"),
361        }
362    }
363
364    #[test]
365    fn test_insert_or_update_struct() {
366        let mutation = insert_or_update_struct(
367            "Guild",
368            TestStruct {
369                struct_field: "abc".to_string(),
370            },
371        );
372        match mutation.operation.unwrap() {
373            v1::mutation::Operation::InsertOrUpdate(w) => assert_struct(w),
374            _ => panic!("invalid operation"),
375        }
376    }
377
378    #[test]
379    fn test_delete() {
380        let mutation = delete("Guild", all_keys());
381        match mutation.operation.unwrap() {
382            v1::mutation::Operation::Delete(w) => {
383                assert_eq!("Guild", w.table);
384                assert!(w.key_set.unwrap().all);
385            }
386            _ => panic!("invalid operation"),
387        }
388    }
389
390    fn assert_struct(mut w: Write) {
391        assert_eq!("Guild", w.table);
392        assert_eq!("StructField", w.columns.pop().unwrap());
393        assert_eq!(
394            "abc",
395            match w.values.pop().unwrap().values.pop().unwrap().kind.unwrap() {
396                Kind::StringValue(v) => v,
397                _ => panic!("error"),
398            }
399        );
400    }
401}