GCC Code Coverage Report


Directory: libimgdoc2/
File: libimgdoc2/src/doc/documentMetadataWriter.cpp
Date: 2025-02-03 12:41:04
Exec Total Coverage
Lines: 114 121 94.2%
Functions: 9 9 100.0%
Branches: 262 542 48.3%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2023 Carl Zeiss Microscopy GmbH
2 //
3 // SPDX-License-Identifier: MIT
4
5 #include "documentMetadataWriter.h"
6 #include <vector>
7 #include <string>
8 #include "gsl/util"
9
10 using namespace std;
11 using namespace imgdoc2;
12
13 634 imgdoc2::dbIndex DocumentMetadataWriter::UpdateOrCreateItem(
14 std::optional<imgdoc2::dbIndex> parent,
15 bool create_node_if_not_exists,
16 const string& name,
17 DocumentMetadataType type,
18 const IDocumentMetadata::metadata_item_variant& value)
19 {
20
2/2
✓ Branch 1 taken 630 times.
✓ Branch 2 taken 4 times.
634 this->CheckNodeNameAndThrowIfInvalid(name);
21
1/2
✓ Branch 1 taken 630 times.
✗ Branch 2 not taken.
630 const DatabaseDataTypeValue item_type = DocumentMetadataBase::DetermineDatabaseDataTypeValueOrThrow(type, value);
22
23
8/10
✓ Branch 1 taken 542 times.
✓ Branch 2 taken 88 times.
✓ Branch 4 taken 542 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 542 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 540 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 628 times.
630 if (parent.has_value() && !this->CheckIfItemExists(parent.value()))
24 {
25
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 ostringstream string_stream;
26
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
2 string_stream << "The parent with pk=" << parent.value() << " does not exist.";
27
3/6
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 throw non_existing_item_exception(string_stream.str(), parent.value());
28 2 }
29
30
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 auto statement = this->CreateStatementForUpdateOrCreateItemAndBindData(create_node_if_not_exists, parent, name, item_type, value);
31
1/2
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 this->GetDocument()->GetDatabase_connection()->Execute(statement.get());
32 628 statement.reset();
33
34 // Ok this worked the new item was inserted or updated. Now we need to get the id of the item.
35 // TODO(Jbl): It is of course a bit unfortunate that we do another lookup here. However, I didn't find a
36 // a way around this (for all the cases we need to support).
37
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 const auto select_statement = this->CreateQueryForNameAndAncestorIdStatement(name, parent);
38
39 // we are expecting exactly one result, or zero in case of "not found"
40
2/4
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 628 times.
628 if (!this->GetDocument()->GetDatabase_connection()->StepStatement(select_statement.get()))
41 {
42 throw std::logic_error("Could not find the item we just inserted or updated");
43 }
44
45
1/2
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
628 const dbIndex pk_of_updated_or_created_item = select_statement->GetResultInt64(0);
46
47 628 return pk_of_updated_or_created_item;
48 628 }
49
50 114 imgdoc2::dbIndex DocumentMetadataWriter::UpdateOrCreateItemForPath(
51 bool create_path_if_not_exists,
52 bool create_node_if_not_exists,
53 const std::string& path,
54 imgdoc2::DocumentMetadataType type,
55 const IDocumentMetadata::metadata_item_variant& value)
56 {
57
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 const auto path_parts = this->SplitPath(path);
58
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 auto pk_of_nodes_on_path = this->GetNodeIdsForPathParts(path_parts);
59
60 // If the size of the "pks of nodes on path" is smaller than the size of the path parts, then we
61 // need to create the missing nodes. Or - only if the caller requested it - we need to create the missing nodes.
62
2/2
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 78 times.
114 if (pk_of_nodes_on_path.size() < path_parts.size() - 1)
63 {
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (!create_path_if_not_exists)
65 {
66 // TODO(JBl): find a better exception type
67 throw std::invalid_argument("The path does not exist and the caller did not request to create it.");
68 }
69
70
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 this->CreateMissingNodesOnPath(path_parts, pk_of_nodes_on_path);
71 }
72
73
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 106 times.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
220 return this->UpdateOrCreateItem(
74 220 pk_of_nodes_on_path.empty() ? optional<dbIndex>(nullopt) : optional<dbIndex>(pk_of_nodes_on_path.back()),
75 create_node_if_not_exists,
76
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
342 string{ path_parts.back() },
77 type,
78 228 value);
79 114 }
80
81 30 uint64_t DocumentMetadataWriter::DeleteItem(
82 std::optional<imgdoc2::dbIndex> primary_key,
83 bool recursively)
84 {
85
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 const auto statement = this->CreateStatementForDeleteItemAndBindData(recursively, primary_key);
86
87 30 int64_t number_of_modified_rows = 0;
88
89 // In the special case "primary_key=nullopt=root" and recursively=false we do not want to delete anything,
90 // and in this case "CreateStatementForDeleteItemAndBindData" returns 0. So, in this corner stone case,
91 // we want to do nothing and return 0.
92
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 4 times.
30 if (statement)
93 {
94
1/2
✓ Branch 6 taken 26 times.
✗ Branch 7 not taken.
26 this->GetDocument()->GetDatabase_connection()->Execute(statement.get(), &number_of_modified_rows);
95 }
96
97 60 return gsl::narrow_cast<uint64_t>(number_of_modified_rows);
98 30 }
99
100 16 uint64_t DocumentMetadataWriter::DeleteItemForPath(
101 const std::string& path,
102 bool recursively)
103 {
104 16 optional<imgdoc2::dbIndex> idx;
105
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 const bool success = this->TryMapPathAndGetTerminalNode(path, &idx);
106
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (success)
107 {
108
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
32 return this->DeleteItem(idx, recursively);
109 }
110
111 // TODO(Jbl): find a better exception type
112 throw runtime_error("DocumentMetadataReader::DeleteItemForPath");
113 }
114
115 628 std::shared_ptr<IDbStatement> DocumentMetadataWriter::CreateStatementForUpdateOrCreateItemAndBindData(bool create_node_if_not_exists, std::optional<imgdoc2::dbIndex> parent, const std::string& name,
116 DatabaseDataTypeValue database_data_type_value,
117 const IDocumentMetadata::metadata_item_variant& value)
118 {
119 628 const bool parent_has_value = parent.has_value();
120
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 ostringstream string_stream;
121
122
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto metadata_table_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetTableNameForMetadataTableOrThrow();
123
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_pk = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Pk);
124
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Name);
125
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_ancestor_id = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_AncestorId);
126
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_type_discriminator = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_TypeDiscriminator);
127
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_value_double = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_ValueDouble);
128
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_value_integer = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_ValueInteger);
129
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_value_string = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_ValueString);
130
131 // CAUTION: it seems if we want to check for a NULL, we cannot simply use the "=" operator, but need to use the "IS" operator.
132 // This is because the "=" operator does not work for NULL values, and it means that we cannot use data-binding, we have to modify
133 // the query string itself. This is not a problem, but it is something to keep in mind.
134 // TODO(Jbl): maybe there is a better way to do this?
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 628 times.
628 if (create_node_if_not_exists == false)
136 {
137 string_stream << "UPDATE [" << metadata_table_name << "] SET " <<
138 "[" << column_name_type_discriminator << "] = ?3, " <<
139 "[" << column_name_value_double << "] = ?4, " <<
140 "[" << column_name_value_integer << "] = ?5, " <<
141 "[" << column_name_value_string << "] = ?6 " <<
142 "WHERE [" << column_name_name << "] = ?1 AND ";
143 if (parent_has_value)
144 {
145 string_stream << "[" << column_name_ancestor_id << "] = ?2";
146 }
147 else
148 {
149 string_stream << "[" << column_name_ancestor_id << "] IS NULL";
150 }
151 }
152 else
153 {
154 string_stream << "INSERT INTO [" << metadata_table_name << "] (" <<
155 "[" << column_name_name << "]," <<
156 "[" << column_name_ancestor_id << "]," <<
157 "[" << column_name_type_discriminator << "]," <<
158 "[" << column_name_value_double << "]," <<
159 "[" << column_name_value_integer << "]," <<
160 "[" << column_name_value_string << "]) " <<
161 "VALUES(" << "?1, ?2, ?3, ?4, ?5, ?6" << ") " <<
162 // There is a constraint on the table that ensures that the combination of name and ancestor id is unique.
163 // So, if the insert fails because of a constraint violation, we update the existing row.
164 "ON CONFLICT([" << column_name_name << "], [" << column_name_ancestor_id << "]) DO UPDATE " <<
165 "SET [" << column_name_type_discriminator << "] = ?3, " <<
166 "[" << column_name_value_double << "] = ?4, " <<
167 "[" << column_name_value_integer << "] = ?5, " <<
168 "[" << column_name_value_string << "] = ?6 " <<
169
44/88
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 628 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 628 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 628 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 628 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 628 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 628 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 628 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 628 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 628 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 628 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 628 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 628 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 628 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 628 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 628 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 628 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 628 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 628 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 628 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 628 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 628 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 628 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 628 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 628 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 628 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 628 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 628 times.
✗ Branch 83 not taken.
✓ Branch 85 taken 628 times.
✗ Branch 86 not taken.
✓ Branch 88 taken 628 times.
✗ Branch 89 not taken.
✓ Branch 91 taken 628 times.
✗ Branch 92 not taken.
✓ Branch 94 taken 628 times.
✗ Branch 95 not taken.
✓ Branch 97 taken 628 times.
✗ Branch 98 not taken.
✓ Branch 100 taken 628 times.
✗ Branch 101 not taken.
✓ Branch 103 taken 628 times.
✗ Branch 104 not taken.
✓ Branch 106 taken 628 times.
✗ Branch 107 not taken.
✓ Branch 109 taken 628 times.
✗ Branch 110 not taken.
✓ Branch 112 taken 628 times.
✗ Branch 113 not taken.
✓ Branch 115 taken 628 times.
✗ Branch 116 not taken.
✓ Branch 118 taken 628 times.
✗ Branch 119 not taken.
✓ Branch 121 taken 628 times.
✗ Branch 122 not taken.
✓ Branch 124 taken 628 times.
✗ Branch 125 not taken.
✓ Branch 127 taken 628 times.
✗ Branch 128 not taken.
✓ Branch 130 taken 628 times.
✗ Branch 131 not taken.
628 "WHERE [" << column_name_name << "] = ?1 AND ";
170
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 88 times.
628 if (parent_has_value)
171 {
172
3/6
✓ Branch 1 taken 540 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 540 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 540 times.
✗ Branch 8 not taken.
540 string_stream << "[" << column_name_ancestor_id << "] = ?2";
173 }
174 else
175 {
176
3/6
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 88 times.
✗ Branch 8 not taken.
88 string_stream << "[" << column_name_ancestor_id << "] IS NULL";
177 }
178 }
179
180
2/4
✓ Branch 5 taken 628 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 628 times.
✗ Branch 9 not taken.
628 auto statement = this->GetDocument()->GetDatabase_connection()->PrepareStatement(string_stream.str());
181
182 628 int binding_index = 1;
183
1/2
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
628 statement->BindString(binding_index++, name);
184
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 88 times.
628 if (parent_has_value)
185 {
186
2/4
✓ Branch 2 taken 540 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 540 times.
✗ Branch 6 not taken.
540 statement->BindInt64(binding_index++, parent.value());
187 }
188 else
189 {
190 88 ++binding_index;
191 }
192
193
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 this->BindTypeDiscriminatorAndData(statement, binding_index, database_data_type_value, value); // this will bind 3 values
194
195 1256 return statement;
196 628 }
197
198 628 std::shared_ptr<IDbStatement> DocumentMetadataWriter::CreateQueryForNameAndAncestorIdStatement(const std::string& name, std::optional<imgdoc2::dbIndex> parent)
199 {
200
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto metadata_table_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetTableNameForMetadataTableOrThrow();
201
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_pk = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Pk);
202
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Name);
203
2/4
✓ Branch 3 taken 628 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 628 times.
✗ Branch 7 not taken.
628 const auto column_name_ancestor_id = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_AncestorId);
204
205 628 const bool parent_has_value = parent.has_value();
206
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 ostringstream string_stream;
207
7/14
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 628 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 628 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 628 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 628 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 628 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 628 times.
✗ Branch 20 not taken.
628 string_stream << "SELECT [" << column_name_pk << "] FROM [" << metadata_table_name << "] WHERE [" << column_name_name << "]=?1 AND ";
208
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 88 times.
628 if (parent_has_value)
209 {
210
3/6
✓ Branch 1 taken 540 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 540 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 540 times.
✗ Branch 8 not taken.
540 string_stream << "[" << column_name_ancestor_id << "] = ?2";
211 }
212 else
213 {
214
3/6
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 88 times.
✗ Branch 8 not taken.
88 string_stream << "[" << column_name_ancestor_id << "] IS NULL";
215 }
216
217
1/2
✓ Branch 1 taken 628 times.
✗ Branch 2 not taken.
628 string_stream << ";";
218
219
2/4
✓ Branch 5 taken 628 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 628 times.
✗ Branch 9 not taken.
628 auto statement = this->GetDocument()->GetDatabase_connection()->PrepareStatement(string_stream.str());
220
1/2
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
628 statement->BindString(1, name);
221
2/2
✓ Branch 0 taken 540 times.
✓ Branch 1 taken 88 times.
628 if (parent_has_value)
222 {
223
2/4
✓ Branch 2 taken 540 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 540 times.
✗ Branch 6 not taken.
540 statement->BindInt64(2, parent.value());
224 }
225
226 1256 return statement;
227 628 }
228
229 /*
230 with recursive paths(id, name, path, level) as (
231 select Pk, Name, Name, 0 from METADATA where AncestorId is null AND Name='Node1'
232 union
233 select METADATA.Pk, METADATA.name, paths.path || '/' || METADATA.name, level+1
234 from METADATA join paths where METADATA.AncestorId = paths.id AND
235 ((level = 0 AND METADATA.Name="Node1_1") OR
236 (level = 1 AND METADATA.Name="Node1_1_2"))
237 )
238 select id, path, level from paths
239 */
240
241 36 void DocumentMetadataWriter::CreateMissingNodesOnPath(const std::vector<std::string_view>& path_parts, std::vector<imgdoc2::dbIndex>& pks_existing)
242 {
243 36 const size_t count_existing_pks = pks_existing.size();
244
2/2
✓ Branch 1 taken 234 times.
✓ Branch 2 taken 36 times.
270 for (size_t i = count_existing_pks; i < path_parts.size() - 1; ++i)
245 {
246
3/4
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 32 times.
✓ Branch 6 taken 234 times.
✗ Branch 7 not taken.
670 const auto new_node = this->UpdateOrCreateItem(
247 202 i > 0 ? std::optional<imgdoc2::dbIndex>(pks_existing[i - 1]) : std::optional<imgdoc2::dbIndex>(),
248 true,
249
1/2
✓ Branch 2 taken 234 times.
✗ Branch 3 not taken.
702 string{ path_parts[i] },
250 DocumentMetadataType::kNull,
251 234 std::monostate());
252
1/2
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
234 pks_existing.push_back(new_node);
253 }
254 36 }
255
256 30 std::shared_ptr<IDbStatement> DocumentMetadataWriter::CreateStatementForDeleteItemAndBindData(bool recursively, std::optional<imgdoc2::dbIndex> parent)
257 {
258
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 ostringstream string_stream;
259
260
2/4
✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
30 const auto metadata_table_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetTableNameForMetadataTableOrThrow();
261
2/4
✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
30 const auto column_name_pk = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Pk);
262
2/4
✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
30 const auto column_name_name = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_Name);
263
2/4
✓ Branch 3 taken 30 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
30 const auto column_name_ancestor_id = this->GetDocument()->GetDataBaseConfigurationCommon()->GetColumnNameOfMetadataTableOrThrow(DatabaseConfigurationCommon::kMetadataTable_Column_AncestorId);
264
265
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 10 times.
30 if (parent.has_value())
266 {
267
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 if (!recursively)
268 {
269 string_stream << "DELETE FROM [" << metadata_table_name << "] WHERE " <<
270 "[" << column_name_pk << "]=?1 AND NOT EXISTS(" <<
271
13/26
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 16 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 16 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 16 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 16 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 16 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 16 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 16 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 16 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 16 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 16 times.
✗ Branch 38 not taken.
16 "SELECT 1 FROM [" << metadata_table_name << "] WHERE " << "[" << column_name_ancestor_id << "]=?1" << ");";
272 }
273 else
274 {
275 // To delete all items below a given node recursively we use a CTE (Common Table Expression) to get all child nodes
276 // of the given node and then delete all items with a PK that is in the result set of the CTE.
277 // The WITH RECURSIVE clause defines a recursive CTE called children that selects all child nodes of the given node
278 // recursively. The first SELECT statement selects all direct child nodes of the given node, while the second SELECT
279 // statement selects all child nodes of the child nodes recursively by joining with the children CTE.
280 // After the children CTE is defined, a separate DELETE statement is executed that deletes all rows in the hierarchy
281 // table that have an id that matches any of the id values returned by the children CTE, and we also delete the
282 // specified node itself.
283 string_stream << "WITH RECURSIVE children(id) AS (" <<
284 "SELECT [" << column_name_pk << "] FROM [" << metadata_table_name << "] WHERE " << "[" << column_name_ancestor_id << "]=?1" <<
285 "UNION ALL " <<
286 "SELECT [" << metadata_table_name << "].[" << column_name_pk << "] FROM [" << metadata_table_name << "] JOIN children ON [" << metadata_table_name << "].[" << column_name_ancestor_id << "]=children.id" <<
287 ") " <<
288
31/62
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 4 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 4 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 4 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 4 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 4 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 4 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 4 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 4 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 4 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 4 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 4 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 4 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 4 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 4 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 4 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 4 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 4 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 4 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 4 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 4 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 4 times.
✗ Branch 83 not taken.
✓ Branch 85 taken 4 times.
✗ Branch 86 not taken.
✓ Branch 88 taken 4 times.
✗ Branch 89 not taken.
✓ Branch 91 taken 4 times.
✗ Branch 92 not taken.
4 "DELETE FROM [" << metadata_table_name << "] WHERE " << "[" << "Pk" << "] IN (SELECT id FROM children) OR " << "[" << column_name_pk << "]=?1;";
289 }
290
291
292
2/4
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 20 times.
✗ Branch 9 not taken.
20 auto statement = this->GetDocument()->GetDatabase_connection()->PrepareStatement(string_stream.str());
293
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
20 statement->BindInt64(1, parent.value());
294 20 return statement;
295 20 }
296 else
297 {
298 // This means that we want to delete the "root" node. It is not possible to delete the "root" itself, but everything in it.
299 // Following this logic, only "recursively=true" is allowed or makes sense.
300
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 if (recursively)
301 {
302 string_stream << "WITH RECURSIVE children(id) AS (" <<
303 "SELECT [" << column_name_pk << "] FROM [" << metadata_table_name << "] WHERE " << "[" << column_name_ancestor_id << "] IS NULL " <<
304 "UNION ALL " <<
305 "SELECT [" << metadata_table_name << "].[" << column_name_pk << "] FROM [" << metadata_table_name << "] JOIN children ON [" << metadata_table_name << "].[" << column_name_ancestor_id << "]=children.id" <<
306 ") " <<
307
31/62
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 6 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 6 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 6 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 6 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 6 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 6 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 6 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 6 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 6 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 6 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 6 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 6 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 6 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 6 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 6 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 6 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 6 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 6 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 6 times.
✗ Branch 77 not taken.
✓ Branch 79 taken 6 times.
✗ Branch 80 not taken.
✓ Branch 82 taken 6 times.
✗ Branch 83 not taken.
✓ Branch 85 taken 6 times.
✗ Branch 86 not taken.
✓ Branch 88 taken 6 times.
✗ Branch 89 not taken.
✓ Branch 91 taken 6 times.
✗ Branch 92 not taken.
6 "DELETE FROM [" << metadata_table_name << "] WHERE " << "[" << column_name_pk << "] IN (SELECT id FROM children) OR " << "[" << column_name_ancestor_id << "] IS NULL;";
308
309
2/4
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
6 return this->GetDocument()->GetDatabase_connection()->PrepareStatement(string_stream.str());
310 }
311 else
312 {
313 4 return nullptr;
314 }
315 }
316 30 }
317
318 634 void DocumentMetadataWriter::CheckNodeNameAndThrowIfInvalid(const std::string& name)
319 {
320 // the string must not be empty and it must not contain a slash
321 // TODO(JBL): is the check for '/' sufficient (for UTF8-strings)?
322
6/6
✓ Branch 1 taken 632 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 630 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 630 times.
634 if (name.empty() || name.find('/') != std::string::npos)
323 {
324
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 throw imgdoc2::invalid_argument_exception("The 'name' must not be empty and it must not contain a slash");
325 }
326 630 }
327