| 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 |