src/render.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /****************************************************************************** | ||
| 2 | ** | ||
| 3 | ** Copyright (C) 2026 Ivan Pinezhaninov <ivan.pinezhaninov@gmail.com> | ||
| 4 | ** | ||
| 5 | ** This file is part of the SQLon - which can be found at | ||
| 6 | ** https://github.com/IvanPinezhaninov/sqlon/. | ||
| 7 | ** | ||
| 8 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 9 | ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 10 | ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| 11 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
| 12 | ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
| 13 | ** OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR | ||
| 14 | ** THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 15 | ** | ||
| 16 | ******************************************************************************/ | ||
| 17 | |||
| 18 | #include <sqlon/error.h> | ||
| 19 | #include <sqlon/prepared_query.h> | ||
| 20 | #include <sqlon/render.h> | ||
| 21 | |||
| 22 | #include <cctype> | ||
| 23 | #include <cmath> | ||
| 24 | #include <cstdint> | ||
| 25 | #include <iomanip> | ||
| 26 | #include <limits> | ||
| 27 | #include <locale> | ||
| 28 | #include <optional> | ||
| 29 | #include <sstream> | ||
| 30 | #include <string> | ||
| 31 | #include <type_traits> | ||
| 32 | #include <unordered_map> | ||
| 33 | #include <unordered_set> | ||
| 34 | #include <utility> | ||
| 35 | #include <vector> | ||
| 36 | |||
| 37 | namespace sqlon { | ||
| 38 | namespace { | ||
| 39 | |||
| 40 | enum class renderer_mode : std::uint8_t { immediate, prepared }; | ||
| 41 | |||
| 42 | enum class expression_context : std::uint8_t { value, result }; | ||
| 43 | |||
| 44 | class renderer final { | ||
| 45 | public: | ||
| 46 | 246 | renderer(const sql_options& options, renderer_mode mode) | |
| 47 | 246 | : m_options(options) | |
| 48 | 246 | , m_mode(mode) | |
| 49 | 246 | {} | |
| 50 | |||
| 51 | 224 | rendered_query render_query(const query& value) | |
| 52 | { | ||
| 53 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
✗ Branch 4 not taken.
|
224 | if (m_mode != renderer_mode::immediate) throw render_error{"renderer is not in immediate mode"}; |
| 54 | 224 | const detail::query_ptr& node = detail::query_access::node(value); | |
| 55 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
✗ Branch 5 not taken.
|
224 | if (!node) throw render_error{"cannot render an empty query node"}; |
| 56 | 224 | collect_parameter_names(*node); | |
| 57 |
1/1✓ Branch 2 taken 158 times.
|
224 | m_result.sql = render_query_node(*node); |
| 58 | 158 | return std::move(m_result); | |
| 59 | } | ||
| 60 | |||
| 61 | 22 | prepared_query render_prepared_query(const query& value) | |
| 62 | { | ||
| 63 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✗ Branch 4 not taken.
|
22 | if (m_mode != renderer_mode::prepared) throw render_error{"renderer is not in prepared mode"}; |
| 64 | 22 | const detail::query_ptr& node = detail::query_access::node(value); | |
| 65 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 5 not taken.
|
22 | if (!node) throw render_error{"cannot prepare an empty query node"}; |
| 66 |
1/1✓ Branch 2 taken 22 times.
|
22 | collect_parameter_names(*node); |
| 67 |
1/1✓ Branch 2 taken 20 times.
|
22 | std::string sql = render_query_node(*node); |
| 68 |
1/1✓ Branch 7 taken 20 times.
|
40 | return prepared_query{std::move(sql), std::move(m_prepared_parameters), std::move(m_prepared_bindings)}; |
| 69 | 20 | } | |
| 70 | |||
| 71 | private: | ||
| 72 | 285 | std::string render_query_node(const detail::query_node& value) | |
| 73 | { | ||
| 74 | 285 | std::string sql = render_ctes(value); | |
| 75 |
4/5✓ Branch 0 taken 196 times.
✓ Branch 1 taken 67 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
|
281 | switch (value.kind) { |
| 76 | 196 | case detail::statement_kind::select: | |
| 77 |
2/2✓ Branch 1 taken 159 times.
✓ Branch 4 taken 159 times.
|
196 | sql += render_select(value); |
| 78 | 159 | return sql; | |
| 79 | 67 | case detail::statement_kind::insert: | |
| 80 |
2/2✓ Branch 1 taken 42 times.
✓ Branch 4 taken 42 times.
|
67 | sql += render_insert(value); |
| 81 | 42 | return sql; | |
| 82 | 12 | case detail::statement_kind::update: | |
| 83 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
|
12 | sql += render_update(value); |
| 84 | 10 | return sql; | |
| 85 | 6 | case detail::statement_kind::delete_: | |
| 86 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
6 | sql += render_delete(value); |
| 87 | 5 | return sql; | |
| 88 | } | ||
| 89 | ✗ | throw render_error{"unknown statement kind"}; | |
| 90 | 65 | } | |
| 91 | |||
| 92 | 196 | std::string render_select(const detail::query_node& value) | |
| 93 | { | ||
| 94 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 195 times.
✓ Branch 5 taken 1 time.
|
196 | if (value.projection.empty()) throw render_error{"SELECT requires at least one projection expression"}; |
| 95 | |||
| 96 |
3/3✓ Branch 0 taken 4 times.
✓ Branch 1 taken 191 times.
✓ Branch 3 taken 195 times.
|
195 | std::string sql = value.distinct ? "SELECT DISTINCT " : "SELECT "; |
| 97 |
7/8✓ Branch 0 taken 6 times.
✓ Branch 1 taken 189 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
|
196 | const bool uses_top = m_options.row_limits == row_limit_style::top_offset_fetch && value.limit && !value.offset && |
| 98 | 1 | value.set_operations.empty(); | |
| 99 |
6/6✓ Branch 0 taken 1 time.
✓ Branch 1 taken 194 times.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 10 taken 1 time.
✓ Branch 13 taken 1 time.
|
195 | if (uses_top) sql += "TOP (" + std::to_string(*value.limit) + ") "; |
| 100 |
2/2✓ Branch 1 taken 264 times.
✓ Branch 2 taken 171 times.
|
435 | for (std::size_t index = 0; index < value.projection.size(); ++index) { |
| 101 |
3/3✓ Branch 0 taken 69 times.
✓ Branch 1 taken 195 times.
✓ Branch 3 taken 69 times.
|
264 | if (index != 0) sql += ", "; |
| 102 |
2/2✓ Branch 2 taken 240 times.
✓ Branch 5 taken 240 times.
|
264 | sql += render_expression(value.projection[index], 0, expression_context::result); |
| 103 | } | ||
| 104 | |||
| 105 |
2/2✓ Branch 1 taken 91 times.
✓ Branch 2 taken 80 times.
|
171 | if (value.from) |
| 106 |
3/3✓ Branch 1 taken 89 times.
✓ Branch 4 taken 89 times.
✓ Branch 7 taken 89 times.
|
91 | sql += " FROM " + render_relation(value.from); |
| 107 | else | ||
| 108 |
2/2✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
|
80 | sql += render_implicit_select_source(); |
| 109 |
5/5✓ Branch 1 taken 46 times.
✓ Branch 2 taken 121 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
✓ Branch 10 taken 44 times.
|
167 | if (value.where) sql += " WHERE " + render_expression(value.where); |
| 110 |
5/5✓ Branch 1 taken 4 times.
✓ Branch 2 taken 161 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
|
165 | if (!value.group_by.empty()) sql += " GROUP BY " + render_expression_list(value.group_by); |
| 111 |
5/5✓ Branch 1 taken 4 times.
✓ Branch 2 taken 161 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
|
165 | if (value.having) sql += " HAVING " + render_expression(value.having); |
| 112 | 165 | std::optional<int> compound_precedence; | |
| 113 |
2/2✓ Branch 4 taken 18 times.
✓ Branch 5 taken 163 times.
|
181 | for (const detail::set_operation_node& operation : value.set_operations) { |
| 114 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 5 not taken.
|
18 | if (!operation.query) throw render_error{"a set operation requires a query"}; |
| 115 | 18 | const int operation_precedence = set_operation_precedence(operation.kind); | |
| 116 |
7/7✓ Branch 1 taken 5 times.
✓ Branch 2 taken 13 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 16 times.
✓ Branch 11 taken 2 times.
|
18 | if (compound_precedence && operation_precedence > *compound_precedence) sql = group_set_sql(std::move(sql)); |
| 117 |
4/4✓ Branch 1 taken 16 times.
✓ Branch 4 taken 16 times.
✓ Branch 7 taken 16 times.
✓ Branch 10 taken 16 times.
|
18 | sql += " " + set_operation_keyword(operation.kind) + " "; |
| 118 |
2/2✓ Branch 2 taken 16 times.
✓ Branch 5 taken 16 times.
|
16 | sql += render_set_operand(*operation.query); |
| 119 | 16 | compound_precedence = operation_precedence; | |
| 120 | } | ||
| 121 | |||
| 122 |
5/5✓ Branch 1 taken 15 times.
✓ Branch 2 taken 148 times.
✓ Branch 4 taken 14 times.
✓ Branch 7 taken 14 times.
✓ Branch 10 taken 14 times.
|
163 | if (!value.order_by.empty()) sql += " ORDER BY " + render_order_by(value.order_by); |
| 123 |
2/2✓ Branch 1 taken 159 times.
✓ Branch 4 taken 159 times.
|
162 | sql += render_row_limit(value, uses_top); |
| 124 | 318 | return sql; | |
| 125 | 36 | } | |
| 126 | |||
| 127 | 84 | std::string render_implicit_select_source() const | |
| 128 | { | ||
| 129 |
2/2✓ Branch 1 taken 70 times.
✓ Branch 2 taken 14 times.
|
84 | if (!m_options.implicit_select_source) return {}; |
| 130 | |||
| 131 |
1/1✓ Branch 2 taken 14 times.
|
14 | std::vector<std::string> qualifiers = m_options.implicit_select_source->identifier_parts; |
| 132 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 13 times.
✓ Branch 5 taken 1 time.
|
14 | if (qualifiers.empty()) throw render_error{"an implicit SELECT source requires an identifier"}; |
| 133 | 13 | const std::string identifier = std::move(qualifiers.back()); | |
| 134 | 13 | qualifiers.pop_back(); | |
| 135 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 12 times.
|
13 | if (m_options.implicit_select_source->quote_identifier) |
| 136 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
|
1 | return " FROM " + render_qualified_identifier(qualifiers, identifier); |
| 137 | |||
| 138 | 12 | std::string source; | |
| 139 |
2/2✓ Branch 4 taken 1 time.
✓ Branch 5 taken 12 times.
|
13 | for (const std::string& qualifier : qualifiers) { |
| 140 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
|
1 | if (!source.empty()) source += "."; |
| 141 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
|
1 | source += render_unquoted_identifier(qualifier); |
| 142 | } | ||
| 143 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
✓ Branch 4 taken 1 time.
|
12 | if (!source.empty()) source += "."; |
| 144 |
3/3✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
✓ Branch 7 taken 11 times.
|
12 | return " FROM " + source + render_unquoted_identifier(identifier); |
| 145 | 16 | } | |
| 146 | |||
| 147 | 162 | std::string render_row_limit(const detail::query_node& value, bool uses_top) const | |
| 148 | { | ||
| 149 |
6/6✓ Branch 1 taken 152 times.
✓ Branch 2 taken 10 times.
✓ Branch 4 taken 144 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 144 times.
✓ Branch 7 taken 18 times.
|
162 | if (!value.limit && !value.offset) return {}; |
| 150 | |||
| 151 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
|
18 | switch (m_options.row_limits) { |
| 152 | 12 | case row_limit_style::limit_offset: { | |
| 153 | 12 | std::string sql; | |
| 154 |
5/5✓ Branch 1 taken 5 times.
✓ Branch 2 taken 7 times.
✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
✓ Branch 11 taken 5 times.
|
12 | if (value.limit) sql += " LIMIT " + std::to_string(*value.limit); |
| 155 |
7/8✓ Branch 1 taken 7 times.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 5 times.
✓ Branch 9 taken 6 times.
✓ Branch 12 taken 6 times.
|
12 | if (!value.limit && value.offset) sql += offset_without_limit_prefix(); |
| 156 |
5/5✓ Branch 1 taken 8 times.
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
✓ Branch 11 taken 8 times.
|
11 | if (value.offset) sql += " OFFSET " + std::to_string(*value.offset); |
| 157 | 11 | return sql; | |
| 158 | 12 | } | |
| 159 | 2 | case row_limit_style::offset_fetch: { | |
| 160 | 2 | std::string sql; | |
| 161 |
6/6✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 11 taken 1 time.
✓ Branch 14 taken 1 time.
|
2 | if (value.offset) sql += " OFFSET " + std::to_string(*value.offset) + " ROWS"; |
| 162 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | if (value.limit) { |
| 163 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 2 times.
|
2 | sql += value.offset ? " FETCH NEXT " : " FETCH FIRST "; |
| 164 |
3/3✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
|
2 | sql += std::to_string(*value.limit) + " ROWS ONLY"; |
| 165 | } | ||
| 166 | 2 | return sql; | |
| 167 | 2 | } | |
| 168 | 3 | case row_limit_style::top_offset_fetch: | |
| 169 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
|
3 | if (uses_top) return {}; |
| 170 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
|
2 | if (value.order_by.empty()) throw render_error{"OFFSET/FETCH requires ORDER BY for the selected preset"}; |
| 171 |
3/3✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
|
2 | return " OFFSET " + std::to_string(value.offset.value_or(0)) + " ROWS" + |
| 172 |
7/14✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 11 taken 1 time.
✓ Branch 15 taken 1 time.
✓ Branch 18 taken 1 time.
✗ Branch 19 not taken.
✓ Branch 21 taken 1 time.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
|
3 | (value.limit ? " FETCH NEXT " + std::to_string(*value.limit) + " ROWS ONLY" : std::string{}); |
| 173 | } | ||
| 174 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"unknown SELECT row-limiting style"}; |
| 175 | } | ||
| 176 | |||
| 177 | 7 | std::string offset_without_limit_prefix() const | |
| 178 | { | ||
| 179 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
|
7 | switch (m_options.offset_without_limit) { |
| 180 | 4 | case offset_without_limit_style::standalone: | |
| 181 | 4 | return {}; | |
| 182 | 1 | case offset_without_limit_style::negative_one_limit: | |
| 183 |
1/1✓ Branch 1 taken 1 time.
|
2 | return " LIMIT -1"; |
| 184 | 1 | case offset_without_limit_style::maximum_unsigned_limit: | |
| 185 |
1/1✓ Branch 1 taken 1 time.
|
2 | return " LIMIT 18446744073709551615"; |
| 186 | } | ||
| 187 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"unknown OFFSET without LIMIT rendering style"}; |
| 188 | } | ||
| 189 | |||
| 190 | 67 | std::string render_insert(const detail::query_node& value) | |
| 191 | { | ||
| 192 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 22 times.
|
67 | if (value.conflict.action != detail::conflict_clause_node::kind::none && |
| 193 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 7 times.
|
45 | value.conflict.action != detail::conflict_clause_node::kind::duplicate_key_update && |
| 194 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 28 times.
|
38 | m_options.conflict_updates == conflict_update_style::merge_statement) { |
| 195 |
1/1✓ Branch 1 taken 6 times.
|
10 | return render_merge_insert(value); |
| 196 | } | ||
| 197 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 22 times.
|
57 | if (value.conflict.action != detail::conflict_clause_node::kind::none && |
| 198 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 7 times.
|
35 | value.conflict.action != detail::conflict_clause_node::kind::duplicate_key_update && |
| 199 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 20 times.
|
28 | m_options.conflict_updates == conflict_update_style::update_or_insert) { |
| 200 |
1/1✓ Branch 1 taken 4 times.
|
8 | return render_update_or_insert(value); |
| 201 | } | ||
| 202 | |||
| 203 |
1/1✓ Branch 1 taken 49 times.
|
49 | const bool uses_inserted_row = conflict_uses_inserted_row(value.conflict); |
| 204 | 49 | std::string inserted_row_alias; | |
| 205 |
4/4✓ Branch 0 taken 13 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 8 times.
|
49 | if (uses_inserted_row && m_options.inserted_row_references == inserted_row_reference_style::values_row_alias) { |
| 206 |
5/6✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 4 times.
|
5 | if (value.default_values || value.insert_source) |
| 207 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"the selected proposed-row reference style requires an INSERT VALUES source"}; |
| 208 |
1/1✓ Branch 1 taken 4 times.
|
4 | inserted_row_alias = make_inserted_row_alias(value.target); |
| 209 | } | ||
| 210 | |||
| 211 |
1/1✓ Branch 1 taken 48 times.
|
48 | std::string sql{"INSERT INTO "}; |
| 212 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 4 taken 48 times.
|
48 | sql += render_mutation_target(value.target); |
| 213 | |||
| 214 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 40 times.
|
48 | if (value.default_values) { |
| 215 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 7 times.
|
8 | if (!has_capability(m_options.capabilities, sql_capability::default_values)) |
| 216 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"DEFAULT VALUES is not supported by the selected preset"}; |
| 217 |
4/8✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 7 times.
|
7 | if (!value.columns.empty() || !value.values.empty() || value.insert_source) |
| 218 | ✗ | throw render_error{"DEFAULT VALUES cannot be combined with another INSERT source"}; | |
| 219 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 6 times.
|
10 | if (value.conflict.action != detail::conflict_clause_node::kind::none && |
| 220 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | !has_capability(m_options.capabilities, sql_capability::default_values_with_conflict)) { |
| 221 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"DEFAULT VALUES cannot be combined with a conflict action for the selected preset"}; |
| 222 | } | ||
| 223 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
|
6 | switch (m_options.default_values) { |
| 224 | 3 | case default_values_style::standard: | |
| 225 |
1/1✓ Branch 1 taken 3 times.
|
3 | sql += " DEFAULT VALUES"; |
| 226 | 3 | break; | |
| 227 | 2 | case default_values_style::empty_parenthesized_values: | |
| 228 |
1/1✓ Branch 1 taken 2 times.
|
2 | sql += " () VALUES ()"; |
| 229 | 2 | break; | |
| 230 | 1 | case default_values_style::unsupported: | |
| 231 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"the selected DEFAULT VALUES capability has no rendering style"}; |
| 232 | } | ||
| 233 | } else { | ||
| 234 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✗ Branch 5 not taken.
|
40 | if (value.columns.empty()) throw render_error{"INSERT requires at least one column"}; |
| 235 |
4/4✓ Branch 1 taken 40 times.
✓ Branch 4 taken 40 times.
✓ Branch 7 taken 40 times.
✓ Branch 10 taken 40 times.
|
40 | sql += " (" + render_column_name_list(value.columns) + ") "; |
| 236 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 36 times.
|
40 | if (value.insert_source) { |
| 237 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
|
4 | if (!has_capability(m_options.capabilities, sql_capability::insert_select)) |
| 238 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"INSERT SELECT is not supported by the selected preset"}; |
| 239 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 5 not taken.
|
3 | if (!value.values.empty()) throw render_error{"INSERT SELECT cannot be combined with VALUES rows"}; |
| 240 |
7/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 2 times.
|
4 | if (value.conflict.action != detail::conflict_clause_node::kind::none && !value.insert_source->where && |
| 241 | 1 | has_capability(m_options.capabilities, sql_capability::insert_select_conflict_requires_where)) { | |
| 242 | throw render_error{"INSERT SELECT with a conflict action requires a source WHERE clause for the selected " | ||
| 243 |
1/1✓ Branch 2 taken 1 time.
|
1 | "preset"}; |
| 244 | } | ||
| 245 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | sql += render_query_node(*value.insert_source); |
| 246 | } else { | ||
| 247 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 5 not taken.
|
36 | if (value.values.empty()) throw render_error{"INSERT requires at least one VALUES row"}; |
| 248 |
6/6✓ Branch 1 taken 8 times.
✓ Branch 2 taken 28 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 34 times.
|
36 | if (value.values.size() > 1 && !has_capability(m_options.capabilities, sql_capability::multi_row_values)) { |
| 249 |
1/1✓ Branch 2 taken 2 times.
|
2 | throw render_error{"multiple INSERT VALUES rows are not supported by the selected preset"}; |
| 250 | } | ||
| 251 |
1/1✓ Branch 1 taken 34 times.
|
34 | sql += "VALUES "; |
| 252 |
2/2✓ Branch 1 taken 40 times.
✓ Branch 2 taken 33 times.
|
73 | for (std::size_t row_index = 0; row_index < value.values.size(); ++row_index) { |
| 253 | 40 | const std::vector<detail::expression_ptr>& row = value.values[row_index]; | |
| 254 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
✗ Branch 6 not taken.
|
40 | if (row.size() != value.columns.size()) throw render_error{"INSERT row width does not match its column list"}; |
| 255 |
3/3✓ Branch 0 taken 6 times.
✓ Branch 1 taken 34 times.
✓ Branch 3 taken 6 times.
|
40 | if (row_index != 0) sql += ", "; |
| 256 |
4/4✓ Branch 1 taken 39 times.
✓ Branch 4 taken 39 times.
✓ Branch 7 taken 39 times.
✓ Branch 10 taken 39 times.
|
40 | sql += "(" + render_value_expression_list(row) + ")"; |
| 257 | } | ||
| 258 |
5/5✓ Branch 1 taken 4 times.
✓ Branch 2 taken 29 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
|
33 | if (!inserted_row_alias.empty()) sql += " AS " + quote_identifier(inserted_row_alias); |
| 259 | } | ||
| 260 | } | ||
| 261 |
3/3✓ Branch 1 taken 40 times.
✓ Branch 4 taken 33 times.
✓ Branch 7 taken 33 times.
|
47 | sql += render_conflict_clause(value, inserted_row_alias, relation_qualifier(value.target)); |
| 262 |
2/2✓ Branch 1 taken 32 times.
✓ Branch 4 taken 32 times.
|
33 | sql += render_returning(value.returning); |
| 263 | 32 | return sql; | |
| 264 | 65 | } | |
| 265 | |||
| 266 | 10 | std::string render_merge_insert(const detail::query_node& value) | |
| 267 | { | ||
| 268 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
|
10 | if (!has_capability(m_options.capabilities, sql_capability::merge)) |
| 269 | ✗ | throw render_error{"MERGE is not supported by the selected preset"}; | |
| 270 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 9 times.
|
10 | if (value.conflict.target.empty()) |
| 271 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a MERGE conflict action requires an explicit conflict target"}; |
| 272 |
5/6✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 8 times.
|
9 | if (value.default_values || value.insert_source) |
| 273 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"generated MERGE statements require an INSERT VALUES source"}; |
| 274 |
3/6✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
|
8 | if (value.columns.empty() || value.values.empty()) |
| 275 | ✗ | throw render_error{"generated MERGE statements require INSERT columns and values"}; | |
| 276 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 5 not taken.
|
8 | if (!value.returning.empty()) throw render_error{"RETURNING is not supported by generated MERGE statements"}; |
| 277 | |||
| 278 |
2/2✓ Branch 5 taken 8 times.
✓ Branch 6 taken 7 times.
|
15 | for (const detail::expression_ptr& target : value.conflict.target) { |
| 279 |
3/3✓ Branch 1 taken 8 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 7 times.
|
8 | if (!contains_column(value.columns, target)) |
| 280 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"MERGE conflict targets must be present in the INSERT column list"}; |
| 281 | } | ||
| 282 |
2/2✓ Branch 5 taken 8 times.
✓ Branch 6 taken 7 times.
|
15 | for (const std::vector<detail::expression_ptr>& row : value.values) { |
| 283 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 6 not taken.
|
8 | if (row.size() != value.columns.size()) throw render_error{"INSERT row width does not match its column list"}; |
| 284 |
2/2✓ Branch 5 taken 18 times.
✓ Branch 6 taken 8 times.
|
26 | for (const detail::expression_ptr& expression : row) |
| 285 |
2/3✓ Branch 1 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
|
18 | if (is_default_value(expression)) |
| 286 | ✗ | throw render_error{"DEFAULT values are not supported in generated MERGE sources"}; | |
| 287 | } | ||
| 288 | |||
| 289 |
1/1✓ Branch 1 taken 7 times.
|
14 | const std::string target_alias{"_sqlon_target"}; |
| 290 |
1/1✓ Branch 1 taken 7 times.
|
14 | const std::string source_alias{"_sqlon_source"}; |
| 291 |
1/1✓ Branch 1 taken 7 times.
|
7 | std::string sql{"MERGE INTO "}; |
| 292 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
|
7 | sql += render_mutation_target(value.target); |
| 293 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
|
7 | if (m_options.merge_target_hint) { |
| 294 |
3/3✓ Branch 2 taken 1 time.
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 1 time.
|
4 | if (m_options.merge_target_hint->empty()) throw render_error{"a MERGE target hint cannot be empty"}; |
| 295 | |||
| 296 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
|
3 | sql += " " + *m_options.merge_target_hint; |
| 297 | } | ||
| 298 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
|
6 | sql += render_relation_alias(target_alias); |
| 299 |
1/1✓ Branch 1 taken 6 times.
|
6 | sql += " USING "; |
| 300 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
|
6 | sql += render_merge_source(value, source_alias); |
| 301 |
1/1✓ Branch 1 taken 6 times.
|
6 | sql += " ON ("; |
| 302 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
|
12 | for (std::size_t index = 0; index < value.conflict.target.size(); ++index) { |
| 303 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
|
6 | if (index != 0) sql += " AND "; |
| 304 |
1/1✓ Branch 2 taken 6 times.
|
6 | const std::string column = render_column_name(value.conflict.target[index]); |
| 305 |
4/4✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 6 times.
|
6 | sql += quote_identifier(target_alias) + "." + column; |
| 306 |
1/1✓ Branch 1 taken 6 times.
|
6 | sql += " = "; |
| 307 |
4/4✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 6 times.
|
6 | sql += quote_identifier(source_alias) + "." + column; |
| 308 | 6 | } | |
| 309 |
1/1✓ Branch 1 taken 6 times.
|
6 | sql += ")"; |
| 310 | |||
| 311 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | if (value.conflict.action == detail::conflict_clause_node::kind::update || |
| 312 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
|
4 | value.conflict.action == detail::conflict_clause_node::kind::update_inserted_values) { |
| 313 |
1/1✓ Branch 1 taken 5 times.
|
5 | sql += " WHEN MATCHED THEN UPDATE SET "; |
| 314 |
5/5✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✓ Branch 13 taken 5 times.
|
10 | sql += render_assignments(conflict_assignments(value), true, source_alias, relation_qualifier(value.target), |
| 315 |
1/1✓ Branch 1 taken 5 times.
|
5 | target_alias); |
| 316 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | } else if (value.conflict.action != detail::conflict_clause_node::kind::do_nothing) { |
| 317 | ✗ | throw render_error{"unknown MERGE conflict action"}; | |
| 318 | } | ||
| 319 | |||
| 320 |
4/4✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 6 times.
|
6 | sql += " WHEN NOT MATCHED THEN INSERT (" + render_column_name_list(value.columns) + ") VALUES ("; |
| 321 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
|
20 | for (std::size_t index = 0; index < value.columns.size(); ++index) { |
| 322 |
3/3✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 8 times.
|
14 | if (index != 0) sql += ", "; |
| 323 |
5/5✓ Branch 2 taken 14 times.
✓ Branch 5 taken 14 times.
✓ Branch 8 taken 14 times.
✓ Branch 11 taken 14 times.
✓ Branch 14 taken 14 times.
|
14 | sql += quote_identifier(source_alias) + "." + render_column_name(value.columns[index]); |
| 324 | } | ||
| 325 |
1/1✓ Branch 1 taken 6 times.
|
6 | sql += ")"; |
| 326 |
3/3✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 3 times.
|
6 | if (m_options.terminate_merge) sql += ";"; |
| 327 | 12 | return sql; | |
| 328 | 9 | } | |
| 329 | |||
| 330 | 6 | std::string render_merge_source(const detail::query_node& value, const std::string& source_alias) | |
| 331 | { | ||
| 332 |
2/3✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | switch (m_options.merge_sources) { |
| 333 | 3 | case merge_source_style::values_table: { | |
| 334 |
1/1✓ Branch 1 taken 3 times.
|
3 | std::string sql{"(VALUES "}; |
| 335 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
|
6 | for (std::size_t row_index = 0; row_index < value.values.size(); ++row_index) { |
| 336 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
|
3 | if (row_index != 0) sql += ", "; |
| 337 |
4/4✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 11 taken 3 times.
|
3 | sql += "(" + render_value_expression_list(value.values[row_index]) + ")"; |
| 338 | } | ||
| 339 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 7 taken 3 times.
✓ Branch 10 taken 3 times.
|
3 | sql += ")" + render_relation_alias(source_alias) + " ("; |
| 340 |
3/3✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 7 taken 3 times.
|
3 | sql += render_column_name_list(value.columns) + ")"; |
| 341 | 3 | return sql; | |
| 342 | 3 | } | |
| 343 | 3 | case merge_source_style::union_all_select: { | |
| 344 |
1/1✓ Branch 1 taken 3 times.
|
3 | std::string sql{"("}; |
| 345 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
|
7 | for (std::size_t row_index = 0; row_index < value.values.size(); ++row_index) { |
| 346 |
3/3✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 time.
|
4 | if (row_index != 0) sql += " UNION ALL "; |
| 347 |
1/1✓ Branch 1 taken 4 times.
|
4 | sql += "SELECT "; |
| 348 | 4 | const std::vector<detail::expression_ptr>& row = value.values[row_index]; | |
| 349 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 4 times.
|
13 | for (std::size_t column_index = 0; column_index < row.size(); ++column_index) { |
| 350 |
3/3✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 5 times.
|
9 | if (column_index != 0) sql += ", "; |
| 351 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 5 taken 9 times.
|
9 | sql += render_value_expression(row[column_index]); |
| 352 |
5/5✓ Branch 0 taken 7 times.
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
|
9 | if (row_index == 0) sql += " AS " + render_column_name(value.columns[column_index]); |
| 353 | } | ||
| 354 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
|
4 | sql += render_implicit_select_source(); |
| 355 | } | ||
| 356 |
3/3✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 7 taken 3 times.
|
3 | return sql + ")" + render_relation_alias(source_alias); |
| 357 | 3 | } | |
| 358 | } | ||
| 359 | ✗ | throw render_error{"unknown MERGE source style"}; | |
| 360 | } | ||
| 361 | |||
| 362 | 44 | static bool contains_column(const std::vector<detail::expression_ptr>& columns, | |
| 363 | const detail::expression_ptr& expected) | ||
| 364 | { | ||
| 365 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
|
44 | if (!expected) return false; |
| 366 |
2/2✓ Branch 5 taken 44 times.
✓ Branch 6 taken 19 times.
|
63 | for (const detail::expression_ptr& column : columns) { |
| 367 |
6/8✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 25 times.
✓ Branch 10 taken 19 times.
✓ Branch 11 taken 25 times.
✓ Branch 12 taken 19 times.
|
69 | if (column && column->kind == detail::expression_kind::column && column->text == expected->text && |
| 368 |
2/3✓ Branch 3 taken 25 times.
✓ Branch 5 taken 25 times.
✗ Branch 6 not taken.
|
25 | column->qualifier == expected->qualifier) { |
| 369 | 25 | return true; | |
| 370 | } | ||
| 371 | } | ||
| 372 | 19 | return false; | |
| 373 | } | ||
| 374 | |||
| 375 | 12 | static std::vector<detail::assignment_node> inserted_value_assignments(const detail::query_node& value) | |
| 376 | { | ||
| 377 | 12 | std::vector<detail::assignment_node> assignments; | |
| 378 |
2/2✓ Branch 5 taken 30 times.
✓ Branch 6 taken 12 times.
|
42 | for (const detail::expression_ptr& column : value.columns) { |
| 379 |
3/3✓ Branch 1 taken 30 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 18 times.
|
30 | if (contains_column(value.conflict.target, column)) continue; |
| 380 |
3/6✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
|
18 | if (!column || column->kind != detail::expression_kind::column) |
| 381 | ✗ | throw render_error{"proposed-value updates require INSERT column expressions"}; | |
| 382 | |||
| 383 |
1/1✓ Branch 1 taken 18 times.
|
18 | std::shared_ptr<detail::expression_node> inserted = std::make_shared<detail::expression_node>(); |
| 384 | 18 | inserted->kind = detail::expression_kind::inserted_column; | |
| 385 |
1/1✓ Branch 3 taken 18 times.
|
18 | inserted->qualifier = column->qualifier; |
| 386 |
1/1✓ Branch 3 taken 18 times.
|
18 | inserted->text = column->text; |
| 387 | 18 | assignments.push_back({column, std::move(inserted)}); | |
| 388 | 18 | } | |
| 389 | 12 | return assignments; | |
| 390 | 18 | } | |
| 391 | |||
| 392 | 14 | static std::vector<detail::assignment_node> conflict_assignments(const detail::query_node& value) | |
| 393 | { | ||
| 394 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
|
14 | if (value.conflict.action == detail::conflict_clause_node::kind::update_inserted_values) |
| 395 | 6 | return inserted_value_assignments(value); | |
| 396 | 8 | return value.conflict.assignments; | |
| 397 | } | ||
| 398 | |||
| 399 | 3 | static bool equivalent_inserted_value_assignments(const std::vector<detail::assignment_node>& actual, | |
| 400 | const std::vector<detail::assignment_node>& expected) | ||
| 401 | { | ||
| 402 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 2 times.
|
3 | if (actual.size() != expected.size()) return false; |
| 403 |
2/2✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 time.
|
3 | for (const detail::assignment_node& expected_assignment : expected) { |
| 404 | 2 | bool found = false; | |
| 405 |
1/2✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
2 | for (const detail::assignment_node& actual_assignment : actual) { |
| 406 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | if (!actual_assignment.column || !expected_assignment.column || |
| 407 |
5/9✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
|
6 | actual_assignment.column->qualifier != expected_assignment.column->qualifier || |
| 408 | 2 | actual_assignment.column->text != expected_assignment.column->text) { | |
| 409 | ✗ | continue; | |
| 410 | } | ||
| 411 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
4 | if (!actual_assignment.value || !expected_assignment.value || |
| 412 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | actual_assignment.value->kind != detail::expression_kind::inserted_column || |
| 413 |
6/9✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 1 time.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
✓ Branch 11 taken 1 time.
✓ Branch 12 taken 1 time.
|
5 | actual_assignment.value->qualifier != expected_assignment.value->qualifier || |
| 414 | 1 | actual_assignment.value->text != expected_assignment.value->text) { | |
| 415 | 1 | return false; | |
| 416 | } | ||
| 417 | 1 | found = true; | |
| 418 | 1 | break; | |
| 419 | } | ||
| 420 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if (!found) return false; |
| 421 | } | ||
| 422 | 1 | return true; | |
| 423 | } | ||
| 424 | |||
| 425 | 18 | static bool is_default_value(const detail::expression_ptr& value) | |
| 426 | { | ||
| 427 |
2/6✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
18 | return value && value->kind == detail::expression_kind::keyword && value->text == "default"; |
| 428 | } | ||
| 429 | |||
| 430 | 12 | std::string render_update(const detail::query_node& value) | |
| 431 | { | ||
| 432 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 5 not taken.
|
12 | if (value.assignments.empty()) throw render_error{"UPDATE requires at least one assignment"}; |
| 433 |
1/1✓ Branch 1 taken 12 times.
|
12 | std::string sql{"UPDATE "}; |
| 434 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
|
12 | sql += render_mutation_target(value.target); |
| 435 |
3/3✓ Branch 4 taken 11 times.
✓ Branch 7 taken 11 times.
✓ Branch 10 taken 11 times.
|
15 | sql += " SET " + render_assignments(value.assignments); |
| 436 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
|
11 | if (value.from) { |
| 437 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if (!has_capability(m_options.capabilities, sql_capability::update_from)) |
| 438 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"UPDATE FROM is not supported by the selected preset"}; |
| 439 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
|
1 | sql += " FROM " + render_relation(value.from); |
| 440 | } | ||
| 441 |
5/5✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
|
10 | if (value.where) sql += " WHERE " + render_expression(value.where); |
| 442 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
|
10 | sql += render_returning(value.returning); |
| 443 | 10 | return sql; | |
| 444 | 2 | } | |
| 445 | |||
| 446 | 8 | std::string render_update_or_insert(const detail::query_node& value) | |
| 447 | { | ||
| 448 | using conflict_kind = detail::conflict_clause_node::kind; | ||
| 449 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
|
8 | if (value.conflict.action != conflict_kind::update && |
| 450 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
|
5 | value.conflict.action != conflict_kind::update_inserted_values) { |
| 451 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"UPDATE OR INSERT requires an update conflict action"}; |
| 452 | } | ||
| 453 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 5 not taken.
|
7 | if (value.conflict.target.empty()) throw render_error{"UPDATE OR INSERT requires an explicit conflict target"}; |
| 454 |
3/6✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
|
7 | if (value.default_values || value.insert_source) |
| 455 | ✗ | throw render_error{"UPDATE OR INSERT requires an INSERT VALUES source"}; | |
| 456 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 5 not taken.
|
7 | if (value.columns.empty()) throw render_error{"UPDATE OR INSERT requires INSERT columns"}; |
| 457 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 6 times.
✓ Branch 5 taken 1 time.
|
7 | if (value.values.size() != 1) throw render_error{"UPDATE OR INSERT requires exactly one VALUES row"}; |
| 458 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
|
6 | if (value.values.front().size() != value.columns.size()) |
| 459 | ✗ | throw render_error{"INSERT row width does not match its column list"}; | |
| 460 |
2/2✓ Branch 5 taken 6 times.
✓ Branch 6 taken 6 times.
|
12 | for (const detail::expression_ptr& target : value.conflict.target) { |
| 461 |
2/3✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
|
6 | if (!contains_column(value.columns, target)) |
| 462 | ✗ | throw render_error{"UPDATE OR INSERT targets must be present in the INSERT column list"}; | |
| 463 | } | ||
| 464 | |||
| 465 |
1/1✓ Branch 1 taken 6 times.
|
6 | const std::vector<detail::assignment_node> expected = inserted_value_assignments(value); |
| 466 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 5 not taken.
|
6 | if (expected.empty()) throw render_error{"UPDATE OR INSERT requires at least one non-target column"}; |
| 467 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
|
9 | if (value.conflict.action == conflict_kind::update && |
| 468 |
3/3✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 time.
|
3 | !equivalent_inserted_value_assignments(value.conflict.assignments, expected)) { |
| 469 |
1/1✓ Branch 2 taken 2 times.
|
2 | throw render_error{"UPDATE OR INSERT can only update every proposed non-target column with its inserted value"}; |
| 470 | } | ||
| 471 | |||
| 472 |
1/1✓ Branch 1 taken 4 times.
|
4 | std::string sql{"UPDATE OR INSERT INTO "}; |
| 473 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
|
4 | sql += render_mutation_target(value.target); |
| 474 |
4/4✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
|
4 | sql += " (" + render_column_name_list(value.columns) + ") VALUES ("; |
| 475 |
3/3✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 4 times.
|
4 | sql += render_value_expression_list(value.values.front()) + ")"; |
| 476 |
4/4✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
|
4 | sql += " MATCHING (" + render_column_name_list(value.conflict.target) + ")"; |
| 477 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
|
4 | sql += render_returning(value.returning); |
| 478 | 8 | return sql; | |
| 479 | 6 | } | |
| 480 | |||
| 481 | 6 | std::string render_delete(const detail::query_node& value) | |
| 482 | { | ||
| 483 |
1/1✓ Branch 1 taken 6 times.
|
6 | std::string sql{"DELETE FROM "}; |
| 484 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 4 taken 6 times.
|
6 | sql += render_mutation_target(value.target); |
| 485 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
|
6 | if (value.from) { |
| 486 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if (!has_capability(m_options.capabilities, sql_capability::delete_using)) |
| 487 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"DELETE USING is not supported by the selected preset"}; |
| 488 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
|
1 | sql += " USING " + render_relation(value.from); |
| 489 | } | ||
| 490 |
4/5✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
|
5 | if (value.where) sql += " WHERE " + render_expression(value.where); |
| 491 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | sql += render_returning(value.returning); |
| 492 | 5 | return sql; | |
| 493 | 1 | } | |
| 494 | |||
| 495 | 77 | std::string render_mutation_target(const detail::relation_ptr& value) const | |
| 496 | { | ||
| 497 |
4/8✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 77 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 77 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 77 times.
|
77 | if (!value || value->kind != detail::relation_kind::table || value->name.empty()) |
| 498 | ✗ | throw render_error{"a mutation statement requires a table target"}; | |
| 499 | |||
| 500 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 77 times.
✗ Branch 6 not taken.
|
77 | if (!value->alias.empty()) throw render_error{"aliased mutation targets are not supported yet"}; |
| 501 | 77 | return render_qualified_identifier(value->qualifiers, value->name); | |
| 502 | } | ||
| 503 | |||
| 504 | 69 | std::string render_column_name_list(const std::vector<detail::expression_ptr>& values) const | |
| 505 | { | ||
| 506 | 69 | std::string sql; | |
| 507 |
2/2✓ Branch 1 taken 118 times.
✓ Branch 2 taken 69 times.
|
187 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 508 |
3/3✓ Branch 0 taken 49 times.
✓ Branch 1 taken 69 times.
✓ Branch 3 taken 49 times.
|
118 | if (index != 0) sql += ", "; |
| 509 |
2/2✓ Branch 2 taken 118 times.
✓ Branch 5 taken 118 times.
|
118 | sql += render_column_name(values[index]); |
| 510 | } | ||
| 511 | 69 | return sql; | |
| 512 | ✗ | } | |
| 513 | |||
| 514 | 181 | std::string render_column_name(const detail::expression_ptr& value) const | |
| 515 | { | ||
| 516 |
4/8✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 181 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 181 times.
|
181 | if (!value || value->kind != detail::expression_kind::column || value->text.empty()) |
| 517 | ✗ | throw render_error{"a mutation column list requires column expressions"}; | |
| 518 | |||
| 519 | 181 | return quote_identifier(value->text); | |
| 520 | } | ||
| 521 | |||
| 522 | 31 | std::string render_assignments(const std::vector<detail::assignment_node>& values, bool conflict = false, | |
| 523 | std::string inserted_row_alias = {}, | ||
| 524 | std::vector<std::string> inserted_target_qualifier = {}, | ||
| 525 | std::string merge_target_alias = {}) | ||
| 526 | { | ||
| 527 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 5 not taken.
|
31 | if (values.empty()) throw render_error{"an assignment list cannot be empty"}; |
| 528 | 31 | const bool previous_conflict_state = m_rendering_conflict_assignments; | |
| 529 | 31 | std::string previous_inserted_row_alias = std::move(m_inserted_row_alias); | |
| 530 | 31 | std::vector<std::string> previous_inserted_target_qualifier = std::move(m_inserted_target_qualifier); | |
| 531 | 31 | std::string previous_merge_target_alias = std::move(m_merge_target_alias); | |
| 532 | 31 | std::vector<std::string> previous_merge_target_qualifier = std::move(m_merge_target_qualifier); | |
| 533 | 31 | m_rendering_conflict_assignments = conflict; | |
| 534 | 31 | m_inserted_row_alias = std::move(inserted_row_alias); | |
| 535 | 31 | m_inserted_target_qualifier = std::move(inserted_target_qualifier); | |
| 536 | 31 | m_merge_target_alias = std::move(merge_target_alias); | |
| 537 |
1/1✓ Branch 1 taken 31 times.
|
31 | m_merge_target_qualifier = m_inserted_target_qualifier; |
| 538 | 31 | std::string sql; | |
| 539 |
2/2✓ Branch 1 taken 36 times.
✓ Branch 2 taken 28 times.
|
64 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 540 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
✗ Branch 6 not taken.
|
36 | if (!values[index].value) throw render_error{"an assignment requires a value"}; |
| 541 |
3/3✓ Branch 0 taken 5 times.
✓ Branch 1 taken 31 times.
✓ Branch 3 taken 5 times.
|
36 | if (index != 0) sql += ", "; |
| 542 |
2/2✓ Branch 2 taken 36 times.
✓ Branch 5 taken 36 times.
|
36 | sql += render_column_name(values[index].column); |
| 543 |
1/1✓ Branch 1 taken 36 times.
|
36 | sql += " = "; |
| 544 |
2/2✓ Branch 2 taken 33 times.
✓ Branch 5 taken 33 times.
|
36 | sql += render_value_expression(values[index].value); |
| 545 | } | ||
| 546 | 28 | m_rendering_conflict_assignments = previous_conflict_state; | |
| 547 | 28 | m_inserted_row_alias = std::move(previous_inserted_row_alias); | |
| 548 | 28 | m_inserted_target_qualifier = std::move(previous_inserted_target_qualifier); | |
| 549 | 28 | m_merge_target_alias = std::move(previous_merge_target_alias); | |
| 550 | 28 | m_merge_target_qualifier = std::move(previous_merge_target_qualifier); | |
| 551 | 56 | return sql; | |
| 552 | 43 | } | |
| 553 | |||
| 554 | 40 | std::string render_conflict_clause(const detail::query_node& query, const std::string& inserted_row_alias, | |
| 555 | const std::vector<std::string>& inserted_target_qualifier) | ||
| 556 | { | ||
| 557 | using conflict_kind = detail::conflict_clause_node::kind; | ||
| 558 | 40 | const detail::conflict_clause_node& value = query.conflict; | |
| 559 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 24 times.
|
40 | if (value.action == conflict_kind::none) return {}; |
| 560 | |||
| 561 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
|
24 | if (value.action == conflict_kind::duplicate_key_update) { |
| 562 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
|
6 | if (!has_capability(m_options.capabilities, sql_capability::on_duplicate_key_update)) |
| 563 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"ON DUPLICATE KEY UPDATE is not supported by the selected preset"}; |
| 564 | |||
| 565 | return " ON DUPLICATE KEY UPDATE " + | ||
| 566 |
4/4✓ Branch 2 taken 5 times.
✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
✓ Branch 11 taken 5 times.
|
5 | render_assignments(value.assignments, true, inserted_row_alias, inserted_target_qualifier); |
| 567 | } | ||
| 568 | |||
| 569 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 15 times.
|
18 | if (!has_capability(m_options.capabilities, sql_capability::on_conflict)) |
| 570 |
1/1✓ Branch 2 taken 3 times.
|
3 | throw render_error{"ON CONFLICT is not supported by the selected preset"}; |
| 571 | |||
| 572 |
1/1✓ Branch 1 taken 15 times.
|
15 | std::string sql{" ON CONFLICT"}; |
| 573 |
6/6✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 12 times.
✓ Branch 7 taken 12 times.
✓ Branch 10 taken 12 times.
✓ Branch 13 taken 12 times.
|
15 | if (!value.target.empty()) sql += " (" + render_column_name_list(value.target) + ")"; |
| 574 |
3/3✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
✓ Branch 3 taken 5 times.
|
15 | if (value.action == conflict_kind::do_nothing) return sql + " DO NOTHING"; |
| 575 | |||
| 576 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
10 | if (value.action == conflict_kind::update || value.action == conflict_kind::update_inserted_values) { |
| 577 |
6/6✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 9 times.
|
12 | if (value.target.empty() && |
| 578 | 2 | has_capability(m_options.capabilities, sql_capability::conflict_update_requires_target)) { | |
| 579 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"ON CONFLICT DO UPDATE requires a conflict target for the selected preset"}; |
| 580 | } | ||
| 581 | |||
| 582 |
1/1✓ Branch 1 taken 7 times.
|
14 | return sql + " DO UPDATE SET " + |
| 583 |
4/4✓ Branch 2 taken 9 times.
✓ Branch 6 taken 9 times.
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
|
31 | render_assignments(conflict_assignments(query), true, {}, inserted_target_qualifier); |
| 584 | } | ||
| 585 | |||
| 586 | ✗ | throw render_error{"unknown conflict action"}; | |
| 587 | 15 | } | |
| 588 | |||
| 589 | 52 | std::string render_returning(const std::vector<detail::expression_ptr>& values) | |
| 590 | { | ||
| 591 |
2/2✓ Branch 1 taken 41 times.
✓ Branch 2 taken 11 times.
|
52 | if (values.empty()) return {}; |
| 592 | |||
| 593 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 10 times.
|
11 | if (!has_capability(m_options.capabilities, sql_capability::returning)) |
| 594 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"RETURNING is not supported by the selected preset"}; |
| 595 | |||
| 596 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
|
10 | return " RETURNING " + render_result_expression_list(values); |
| 597 | } | ||
| 598 | |||
| 599 | 285 | std::string render_ctes(const detail::query_node& value) | |
| 600 | { | ||
| 601 |
2/2✓ Branch 1 taken 273 times.
✓ Branch 2 taken 12 times.
|
285 | if (value.ctes.empty()) { |
| 602 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 273 times.
✗ Branch 4 not taken.
|
273 | if (value.recursive) throw render_error{"a recursive query requires at least one CTE"}; |
| 603 | 273 | return {}; | |
| 604 | } | ||
| 605 | |||
| 606 |
6/6✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 11 times.
|
12 | if (value.recursive && !has_capability(m_options.capabilities, sql_capability::recursive_cte)) |
| 607 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"recursive CTEs are not supported by the selected SQL options"}; |
| 608 | |||
| 609 |
1/1✓ Branch 1 taken 11 times.
|
11 | std::string sql{"WITH "}; |
| 610 |
5/5✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 5 taken 3 times.
|
11 | if (value.recursive && m_options.recursive_ctes == recursive_cte_style::recursive_keyword) sql = "WITH RECURSIVE "; |
| 611 | 11 | std::unordered_set<std::string> cte_names; | |
| 612 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 8 times.
|
22 | for (std::size_t index = 0; index < value.ctes.size(); ++index) { |
| 613 | 14 | const detail::common_table_expression_node& cte = value.ctes[index]; | |
| 614 |
3/7✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 10 not taken.
|
14 | if (cte.name.empty() || !cte.query) throw render_error{"a CTE requires a name and query"}; |
| 615 |
4/4✓ Branch 1 taken 14 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 13 times.
✓ Branch 7 taken 1 time.
|
14 | if (!cte_names.emplace(cte.name).second) throw render_error{"CTE names must be unique within a query"}; |
| 616 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 11 times.
|
16 | if (cte.query->kind != detail::statement_kind::select && |
| 617 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
|
3 | !has_capability(m_options.capabilities, sql_capability::data_modifying_cte)) { |
| 618 |
1/1✓ Branch 2 taken 2 times.
|
2 | throw render_error{"data-modifying CTEs are not supported by the selected preset"}; |
| 619 | } | ||
| 620 |
3/3✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 2 times.
|
11 | if (index != 0) sql += ", "; |
| 621 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
|
11 | sql += quote_identifier(cte.name); |
| 622 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 4 times.
|
11 | if (!cte.columns.empty()) { |
| 623 |
1/1✓ Branch 1 taken 7 times.
|
7 | sql += " ("; |
| 624 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
|
14 | for (std::size_t column_index = 0; column_index < cte.columns.size(); ++column_index) { |
| 625 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
|
7 | if (column_index != 0) sql += ", "; |
| 626 |
2/2✓ Branch 2 taken 7 times.
✓ Branch 5 taken 7 times.
|
7 | sql += quote_identifier(cte.columns[column_index]); |
| 627 | } | ||
| 628 |
1/1✓ Branch 1 taken 7 times.
|
7 | sql += ")"; |
| 629 | } | ||
| 630 | |||
| 631 |
1/1✓ Branch 1 taken 11 times.
|
11 | sql += " AS ("; |
| 632 |
2/2✓ Branch 2 taken 11 times.
✓ Branch 5 taken 11 times.
|
11 | sql += render_query_node(*cte.query); |
| 633 |
1/1✓ Branch 1 taken 11 times.
|
11 | sql += ")"; |
| 634 | } | ||
| 635 |
1/1✓ Branch 1 taken 8 times.
|
8 | return sql + " "; |
| 636 | 14 | } | |
| 637 | |||
| 638 | 18 | std::string set_operation_keyword(detail::set_operation_kind kind) const | |
| 639 | { | ||
| 640 |
4/5✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
18 | switch (kind) { |
| 641 | 5 | case detail::set_operation_kind::union_: | |
| 642 |
1/1✓ Branch 1 taken 5 times.
|
10 | return "UNION"; |
| 643 | 6 | case detail::set_operation_kind::union_all: | |
| 644 |
1/1✓ Branch 1 taken 6 times.
|
12 | return "UNION ALL"; |
| 645 | 4 | case detail::set_operation_kind::intersect: | |
| 646 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
|
4 | if (!has_capability(m_options.capabilities, sql_capability::intersect)) |
| 647 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"INTERSECT is not supported by the selected SQL options"}; |
| 648 |
1/1✓ Branch 1 taken 3 times.
|
6 | return "INTERSECT"; |
| 649 | 3 | case detail::set_operation_kind::except: | |
| 650 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | if (!has_capability(m_options.capabilities, sql_capability::except)) |
| 651 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"EXCEPT is not supported by the selected SQL options"}; |
| 652 |
3/3✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 2 times.
|
4 | return m_options.except_operator == except_style::minus_keyword ? "MINUS" : "EXCEPT"; |
| 653 | } | ||
| 654 | ✗ | throw render_error{"unknown set operation kind"}; | |
| 655 | } | ||
| 656 | |||
| 657 | 18 | static int set_operation_precedence(detail::set_operation_kind kind) | |
| 658 | { | ||
| 659 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14 times.
|
18 | return kind == detail::set_operation_kind::intersect ? 20 : 10; |
| 660 | } | ||
| 661 | |||
| 662 | 16 | std::string render_set_operand(const detail::query_node& value) | |
| 663 | { | ||
| 664 | 16 | std::string sql = render_query_node(value); | |
| 665 |
9/12✓ Branch 1 taken 14 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1 time.
✓ Branch 7 taken 13 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 13 times.
✓ Branch 15 taken 3 times.
✓ Branch 16 taken 13 times.
|
16 | if (!value.set_operations.empty() || !value.order_by.empty() || value.limit || value.offset || !value.ctes.empty()) |
| 666 |
1/1✓ Branch 3 taken 3 times.
|
3 | sql = group_set_sql(std::move(sql)); |
| 667 | 16 | return sql; | |
| 668 | ✗ | } | |
| 669 | |||
| 670 | 5 | std::string group_set_sql(std::string sql) const | |
| 671 | { | ||
| 672 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 3 times.
|
5 | if (m_options.set_operands == set_operand_style::parenthesized) return "(" + sql + ")"; |
| 673 |
5/5✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
|
4 | return "SELECT * FROM (" + sql + ")" + render_relation_alias("_sqlon_set_operand"); |
| 674 | } | ||
| 675 | |||
| 676 | 23 | std::string render_expression_list(const std::vector<detail::expression_ptr>& values) | |
| 677 | { | ||
| 678 | 23 | std::string sql; | |
| 679 |
2/2✓ Branch 1 taken 23 times.
✓ Branch 2 taken 23 times.
|
46 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 680 |
3/3✓ Branch 0 taken 9 times.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 9 times.
|
23 | if (index != 0) sql += ", "; |
| 681 |
2/2✓ Branch 2 taken 23 times.
✓ Branch 5 taken 23 times.
|
23 | sql += render_expression(values[index]); |
| 682 | } | ||
| 683 | 23 | return sql; | |
| 684 | ✗ | } | |
| 685 | |||
| 686 | 10 | std::string render_result_expression_list(const std::vector<detail::expression_ptr>& values) | |
| 687 | { | ||
| 688 | 10 | std::string sql; | |
| 689 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
|
20 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 690 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
|
10 | if (index != 0) sql += ", "; |
| 691 |
2/2✓ Branch 2 taken 10 times.
✓ Branch 5 taken 10 times.
|
10 | sql += render_expression(values[index], 0, expression_context::result); |
| 692 | } | ||
| 693 | 10 | return sql; | |
| 694 | ✗ | } | |
| 695 | |||
| 696 | 47 | std::string render_value_expression_list(const std::vector<detail::expression_ptr>& values) | |
| 697 | { | ||
| 698 | 47 | std::string sql; | |
| 699 |
2/2✓ Branch 1 taken 91 times.
✓ Branch 2 taken 46 times.
|
137 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 700 |
3/3✓ Branch 0 taken 44 times.
✓ Branch 1 taken 47 times.
✓ Branch 3 taken 44 times.
|
91 | if (index != 0) sql += ", "; |
| 701 |
2/2✓ Branch 2 taken 90 times.
✓ Branch 5 taken 90 times.
|
91 | sql += render_value_expression(values[index]); |
| 702 | } | ||
| 703 | 46 | return sql; | |
| 704 | 1 | } | |
| 705 | |||
| 706 | 136 | std::string render_value_expression(const detail::expression_ptr& value) | |
| 707 | { | ||
| 708 |
7/8✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 130 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 5 times.
✓ Branch 11 taken 131 times.
|
136 | if (value && value->kind == detail::expression_kind::keyword && value->text == "default") { |
| 709 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
|
5 | if (!has_capability(m_options.capabilities, sql_capability::default_value)) |
| 710 |
1/1✓ Branch 2 taken 2 times.
|
2 | throw render_error{"DEFAULT values are not supported by the selected preset"}; |
| 711 |
1/1✓ Branch 1 taken 3 times.
|
6 | return "DEFAULT"; |
| 712 | } | ||
| 713 | 131 | return render_expression(value); | |
| 714 | } | ||
| 715 | |||
| 716 | 19 | std::string render_order_by(const std::vector<detail::order_by_node>& values) | |
| 717 | { | ||
| 718 | 19 | std::string sql; | |
| 719 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 18 times.
|
39 | for (std::size_t index = 0; index < values.size(); ++index) { |
| 720 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 6 not taken.
|
21 | if (!values[index].expression) throw render_error{"ORDER BY requires an expression"}; |
| 721 |
4/4✓ Branch 1 taken 6 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 20 times.
|
27 | if (values[index].nulls != detail::nulls_order::unspecified && |
| 722 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
|
6 | !has_capability(m_options.capabilities, sql_capability::nulls_ordering)) { |
| 723 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"NULLS FIRST/LAST is not supported by the selected SQL options"}; |
| 724 | } | ||
| 725 |
3/3✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 2 times.
|
20 | if (index != 0) sql += ", "; |
| 726 |
2/2✓ Branch 2 taken 20 times.
✓ Branch 5 taken 20 times.
|
20 | sql += render_expression(values[index].expression); |
| 727 |
3/3✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
✓ Branch 4 taken 20 times.
|
20 | sql += values[index].direction == detail::order_direction::descending ? " DESC" : " ASC"; |
| 728 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 19 times.
✓ Branch 4 taken 1 time.
|
20 | if (values[index].nulls == detail::nulls_order::first) sql += " NULLS FIRST"; |
| 729 |
3/3✓ Branch 1 taken 4 times.
✓ Branch 2 taken 16 times.
✓ Branch 4 taken 4 times.
|
20 | if (values[index].nulls == detail::nulls_order::last) sql += " NULLS LAST"; |
| 730 | } | ||
| 731 | 18 | return sql; | |
| 732 | 1 | } | |
| 733 | |||
| 734 | 892 | std::string render_expression(const detail::expression_ptr& value, int parent_precedence = 0, | |
| 735 | expression_context context = expression_context::value) | ||
| 736 | { | ||
| 737 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 892 times.
✗ Branch 5 not taken.
|
892 | if (!value) throw render_error{"cannot render an empty expression node"}; |
| 738 | |||
| 739 |
19/19✓ Branch 1 taken 278 times.
✓ Branch 2 taken 270 times.
✓ Branch 3 taken 34 times.
✓ Branch 4 taken 41 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 16 times.
✓ Branch 8 taken 118 times.
✓ Branch 9 taken 9 times.
✓ Branch 10 taken 27 times.
✓ Branch 11 taken 15 times.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 17 times.
✓ Branch 14 taken 2 times.
✓ Branch 15 taken 27 times.
✓ Branch 16 taken 1 time.
✓ Branch 17 taken 6 times.
✓ Branch 18 taken 2 times.
✓ Branch 19 taken 1 time.
|
892 | switch (value->kind) { |
| 740 | 278 | case detail::expression_kind::column: | |
| 741 | 278 | return render_column(*value); | |
| 742 | 270 | case detail::expression_kind::parameter: | |
| 743 | 270 | return render_parameter(*value); | |
| 744 | 34 | case detail::expression_kind::parameter_slot: | |
| 745 | 34 | return render_parameter_slot(*value); | |
| 746 | 41 | case detail::expression_kind::literal: | |
| 747 | 41 | return render_literal(*value); | |
| 748 | 20 | case detail::expression_kind::inserted_column: | |
| 749 | 20 | return render_inserted_column(*value); | |
| 750 | 6 | case detail::expression_kind::keyword: | |
| 751 | 6 | return render_keyword(*value); | |
| 752 | 16 | case detail::expression_kind::alias: | |
| 753 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 6 not taken.
|
16 | if (value->operands.size() != 1) throw render_error{"an aliased expression requires one operand"}; |
| 754 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 6 not taken.
|
16 | if (value->text.empty()) throw render_error{"an aliased expression requires a name"}; |
| 755 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
|
16 | if (context == expression_context::result) |
| 756 |
4/4✓ Branch 2 taken 14 times.
✓ Branch 7 taken 14 times.
✓ Branch 10 taken 14 times.
✓ Branch 13 taken 14 times.
|
14 | return render_expression(value->operands.front()) + " AS " + quote_identifier(value->text); |
| 757 | 2 | return render_expression(value->operands.front(), parent_precedence); | |
| 758 | 118 | case detail::expression_kind::binary: | |
| 759 | 118 | return render_binary(*value, parent_precedence); | |
| 760 | 9 | case detail::expression_kind::unary: | |
| 761 | 9 | return render_unary(*value, parent_precedence); | |
| 762 | 27 | case detail::expression_kind::function: | |
| 763 | 27 | return render_function(*value); | |
| 764 | 15 | case detail::expression_kind::function_call: | |
| 765 | 15 | return render_function_call(*value); | |
| 766 | 2 | case detail::expression_kind::distinct: | |
| 767 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 6 not taken.
|
2 | if (value->operands.size() != 1) throw render_error{"DISTINCT requires one expression"}; |
| 768 |
2/2✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
|
2 | return "DISTINCT " + render_expression(value->operands.front()); |
| 769 | 17 | case detail::expression_kind::window: | |
| 770 | 17 | return render_window(*value); | |
| 771 | 2 | case detail::expression_kind::case_: | |
| 772 | 2 | return render_case(*value); | |
| 773 | 27 | case detail::expression_kind::raw_expression: | |
| 774 | 27 | return render_raw_expression(*value); | |
| 775 | 1 | case detail::expression_kind::raw: | |
| 776 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 6 not taken.
|
1 | if (value->text.empty()) throw render_error{"a raw SQL expression cannot be empty"}; |
| 777 | 1 | return value->text; | |
| 778 | 6 | case detail::expression_kind::subquery: | |
| 779 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 6 not taken.
|
6 | if (!value->subquery) throw render_error{"a subquery expression requires a query"}; |
| 780 |
3/3✓ Branch 3 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 9 taken 5 times.
|
6 | return "(" + render_query_node(*value->subquery) + ")"; |
| 781 | 2 | case detail::expression_kind::exists: | |
| 782 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 6 not taken.
|
2 | if (!value->subquery) throw render_error{"EXISTS requires a subquery"}; |
| 783 |
3/3✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
|
2 | return "EXISTS (" + render_query_node(*value->subquery) + ")"; |
| 784 | 1 | default: | |
| 785 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"this expression kind is not implemented yet"}; |
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | 27 | std::string render_raw_expression(const detail::expression_node& value) | |
| 790 | { | ||
| 791 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 26 times.
✓ Branch 5 taken 1 time.
|
27 | if (value.text.empty()) throw render_error{"a raw expression template cannot be empty"}; |
| 792 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 25 times.
✓ Branch 5 taken 1 time.
|
26 | if (value.operands.empty()) throw render_error{"a raw expression requires at least one operand"}; |
| 793 | |||
| 794 |
1/1✓ Branch 1 taken 25 times.
|
25 | std::string sql{"("}; |
| 795 | 25 | std::size_t operand_index = 0; | |
| 796 |
2/2✓ Branch 1 taken 218 times.
✓ Branch 2 taken 22 times.
|
240 | for (std::size_t index = 0; index < value.text.size(); ++index) { |
| 797 | 218 | const char character = value.text[index]; | |
| 798 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 171 times.
|
218 | if (character == '{') { |
| 799 |
5/6✓ Branch 1 taken 46 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 46 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 46 times.
|
47 | if (index + 1 >= value.text.size() || value.text[index + 1] != '}') |
| 800 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a raw expression template contains malformed syntax"}; |
| 801 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 45 times.
|
46 | if (operand_index >= value.operands.size()) |
| 802 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a raw expression has fewer operands than {} positions"}; |
| 803 |
2/2✓ Branch 2 taken 45 times.
✓ Branch 5 taken 45 times.
|
45 | sql += render_expression(value.operands[operand_index++]); |
| 804 | 45 | ++index; | |
| 805 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 170 times.
|
171 | } else if (character == '}') { |
| 806 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a raw expression template contains malformed syntax"}; |
| 807 | } else { | ||
| 808 |
1/1✓ Branch 1 taken 170 times.
|
170 | sql += character; |
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 21 times.
|
22 | if (operand_index != value.operands.size()) |
| 813 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a raw expression has more operands than {} positions"}; |
| 814 |
1/1✓ Branch 1 taken 21 times.
|
42 | return sql + ")"; |
| 815 | 25 | } | |
| 816 | |||
| 817 | 118 | std::string render_binary(const detail::expression_node& value, int parent_precedence) | |
| 818 | { | ||
| 819 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 118 times.
✗ Branch 5 not taken.
|
118 | if (value.operands.size() != 2) throw render_error{"a binary expression requires two operands"}; |
| 820 | 118 | const int precedence = binary_precedence(value.text); | |
| 821 | 118 | std::string sql = render_expression(value.operands[0], precedence); | |
| 822 |
4/4✓ Branch 1 taken 118 times.
✓ Branch 4 taken 118 times.
✓ Branch 7 taken 118 times.
✓ Branch 10 taken 118 times.
|
118 | sql += " " + binary_operator(value.text) + " "; |
| 823 |
2/2✓ Branch 2 taken 116 times.
✓ Branch 5 taken 116 times.
|
118 | sql += render_expression(value.operands[1], precedence + 1); |
| 824 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 113 times.
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 3 times.
|
116 | if (precedence < parent_precedence) sql = "(" + sql + ")"; |
| 825 | 116 | return sql; | |
| 826 | 2 | } | |
| 827 | |||
| 828 | 9 | std::string render_unary(const detail::expression_node& value, int parent_precedence) | |
| 829 | { | ||
| 830 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 5 not taken.
|
9 | if (value.operands.size() != 1) throw render_error{"a unary expression requires one operand"}; |
| 831 |
5/5✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 11 taken 3 times.
|
9 | if (value.text == "group") return "(" + render_expression(value.operands[0]) + ")"; |
| 832 |
4/4✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
|
6 | if (value.text == "is_null") return render_expression(value.operands[0], 30) + " IS NULL"; |
| 833 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
|
5 | if (value.text == "is_not_null") return render_expression(value.operands[0], 30) + " IS NOT NULL"; |
| 834 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | if (value.text == "not") { |
| 835 | 2 | constexpr int precedence = 25; | |
| 836 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | std::string sql = "NOT " + render_expression(value.operands[0], precedence); |
| 837 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
|
2 | if (precedence < parent_precedence) sql = "(" + sql + ")"; |
| 838 | 2 | return sql; | |
| 839 | 2 | } | |
| 840 | |||
| 841 | ✗ | throw render_error{"unknown unary expression operation"}; | |
| 842 | } | ||
| 843 | |||
| 844 | 27 | std::string render_function(const detail::expression_node& value) | |
| 845 | { | ||
| 846 |
6/6✓ Branch 1 taken 23 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 21 times.
|
27 | if (value.text == "in" || value.text == "not_in") { |
| 847 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 5 not taken.
|
6 | if (value.operands.size() < 2) throw render_error{"IN requires at least one value"}; |
| 848 |
5/6✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 3 times.
|
6 | if (value.operands.size() == 2 && value.operands[1]->kind == detail::expression_kind::subquery) { |
| 849 |
1/1✓ Branch 2 taken 3 times.
|
3 | std::string sql = render_expression(value.operands.front(), 30); |
| 850 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 3 times.
|
3 | sql += value.text == "not_in" ? " NOT IN " : " IN "; |
| 851 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
|
3 | sql += render_expression(value.operands[1]); |
| 852 | 3 | return sql; | |
| 853 | 3 | } | |
| 854 | |||
| 855 |
1/1✓ Branch 2 taken 3 times.
|
3 | std::string sql = render_expression(value.operands.front(), 30); |
| 856 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 3 times.
|
3 | sql += value.text == "not_in" ? " NOT IN (" : " IN ("; |
| 857 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
|
12 | for (std::size_t index = 1; index < value.operands.size(); ++index) { |
| 858 |
3/3✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 6 times.
|
9 | if (index != 1) sql += ", "; |
| 859 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 5 taken 9 times.
|
9 | sql += render_expression(value.operands[index]); |
| 860 | } | ||
| 861 |
1/1✓ Branch 1 taken 3 times.
|
3 | return sql + ")"; |
| 862 | 3 | } | |
| 863 | |||
| 864 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 19 times.
|
21 | if (value.text == "between") { |
| 865 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 5 not taken.
|
2 | if (value.operands.size() != 3) throw render_error{"BETWEEN requires a lower and upper bound"}; |
| 866 |
1/1✓ Branch 2 taken 2 times.
|
2 | std::string sql = render_expression(value.operands[0], 30); |
| 867 |
1/1✓ Branch 1 taken 2 times.
|
2 | sql += " BETWEEN "; |
| 868 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | sql += render_expression(value.operands[1]); |
| 869 |
1/1✓ Branch 1 taken 2 times.
|
2 | sql += " AND "; |
| 870 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | sql += render_expression(value.operands[2]); |
| 871 | 2 | return sql; | |
| 872 | 2 | } | |
| 873 | |||
| 874 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 18 times.
|
19 | if (value.text == "count_all") { |
| 875 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 5 not taken.
|
1 | if (!value.operands.empty()) throw render_error{"COUNT(*) does not accept operands"}; |
| 876 |
1/1✓ Branch 1 taken 1 time.
|
2 | return "COUNT(*)"; |
| 877 | } | ||
| 878 | |||
| 879 |
9/10✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 8 times.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 1 time.
✓ Branch 10 taken 1 time.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 18 times.
✗ Branch 13 not taken.
|
19 | if (value.text == "count" || value.text == "sum" || value.text == "min" || value.text == "max" || |
| 880 |
1/2✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
|
1 | value.text == "avg") { |
| 881 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 5 not taken.
|
18 | if (value.operands.size() != 1) throw render_error{"an aggregate function requires one operand"}; |
| 882 |
1/1✓ Branch 1 taken 18 times.
|
18 | std::string name = value.text; |
| 883 |
2/2✓ Branch 4 taken 66 times.
✓ Branch 5 taken 18 times.
|
84 | for (char& character : name) |
| 884 | 66 | character = static_cast<char>(std::toupper(static_cast<unsigned char>(character))); | |
| 885 |
4/4✓ Branch 2 taken 18 times.
✓ Branch 5 taken 18 times.
✓ Branch 8 taken 18 times.
✓ Branch 11 taken 18 times.
|
18 | return name + "(" + render_expression(value.operands.front()) + ")"; |
| 886 | 18 | } | |
| 887 | |||
| 888 | ✗ | throw render_error{"unknown SQL function expression"}; | |
| 889 | } | ||
| 890 | |||
| 891 | 15 | std::string render_function_call(const detail::expression_node& value) | |
| 892 | { | ||
| 893 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 6 not taken.
|
15 | if (!detail::valid_function_name(value.text)) throw render_error{"a SQL function has an invalid name"}; |
| 894 |
1/1✓ Branch 1 taken 15 times.
|
15 | std::string name = value.text; |
| 895 |
2/2✓ Branch 4 taken 90 times.
✓ Branch 5 taken 15 times.
|
105 | for (char& character : name) |
| 896 | 90 | character = static_cast<char>(std::toupper(static_cast<unsigned char>(character))); | |
| 897 |
4/4✓ Branch 1 taken 15 times.
✓ Branch 4 taken 15 times.
✓ Branch 7 taken 15 times.
✓ Branch 10 taken 15 times.
|
30 | return name + "(" + render_expression_list(value.operands) + ")"; |
| 898 | 15 | } | |
| 899 | |||
| 900 | 17 | std::string render_window(const detail::expression_node& value) | |
| 901 | { | ||
| 902 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 16 times.
|
17 | if (!has_capability(m_options.capabilities, sql_capability::window_functions)) |
| 903 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"window functions are not supported by the selected preset"}; |
| 904 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
|
16 | if (value.operands.size() != 1 || !value.window) |
| 905 | ✗ | throw render_error{"a window expression requires one expression and a window specification"}; | |
| 906 |
5/5✓ Branch 2 taken 16 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 13 times.
|
20 | if (contains_distinct(value.operands.front()) && |
| 907 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
|
4 | !has_capability(m_options.capabilities, sql_capability::distinct_window_aggregates)) { |
| 908 |
1/1✓ Branch 2 taken 3 times.
|
3 | throw render_error{"DISTINCT window aggregates are not supported by the selected preset"}; |
| 909 | } | ||
| 910 | |||
| 911 | 13 | const detail::window_spec_node& window = *value.window; | |
| 912 |
2/2✓ Branch 2 taken 13 times.
✓ Branch 5 taken 13 times.
|
13 | std::string sql = render_expression(value.operands.front()) + " OVER ("; |
| 913 | 13 | bool has_clause = false; | |
| 914 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
|
13 | if (!window.partition_by.empty()) { |
| 915 |
3/3✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
|
4 | sql += "PARTITION BY " + render_expression_list(window.partition_by); |
| 916 | 4 | has_clause = true; | |
| 917 | } | ||
| 918 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
|
13 | if (!window.order_by.empty()) { |
| 919 |
2/3✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (has_clause) sql += " "; |
| 920 |
3/3✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 7 taken 4 times.
|
4 | sql += "ORDER BY " + render_order_by(window.order_by); |
| 921 | 4 | has_clause = true; | |
| 922 | } | ||
| 923 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 6 times.
|
13 | if (window.frame) { |
| 924 |
3/3✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 1 time.
|
7 | if (has_clause) sql += " "; |
| 925 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 5 taken 6 times.
|
7 | sql += render_window_frame(*window.frame); |
| 926 | } | ||
| 927 |
1/1✓ Branch 1 taken 12 times.
|
24 | return sql + ")"; |
| 928 | 13 | } | |
| 929 | |||
| 930 | 7 | std::string render_window_frame(const detail::window_frame_node& value) | |
| 931 | { | ||
| 932 | 7 | std::string sql; | |
| 933 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
7 | switch (value.mode) { |
| 934 | 4 | case detail::window_frame_mode::rows: | |
| 935 |
1/1✓ Branch 1 taken 4 times.
|
4 | sql = "ROWS "; |
| 936 | 4 | break; | |
| 937 | 1 | case detail::window_frame_mode::range: | |
| 938 |
1/1✓ Branch 1 taken 1 time.
|
1 | sql = "RANGE "; |
| 939 | 1 | break; | |
| 940 | 2 | case detail::window_frame_mode::groups: | |
| 941 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if (!has_capability(m_options.capabilities, sql_capability::groups_window_frame)) |
| 942 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"GROUPS window frames are not supported by the selected preset"}; |
| 943 |
1/1✓ Branch 1 taken 1 time.
|
1 | sql = "GROUPS "; |
| 944 | 1 | break; | |
| 945 | } | ||
| 946 | |||
| 947 |
4/4✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 3 times.
✓ Branch 7 taken 3 times.
|
6 | if (!value.end) return sql + render_window_bound(value.start); |
| 948 |
6/6✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
✓ Branch 11 taken 3 times.
✓ Branch 14 taken 3 times.
✓ Branch 17 taken 3 times.
|
3 | return sql + "BETWEEN " + render_window_bound(value.start) + " AND " + render_window_bound(*value.end); |
| 949 | 7 | } | |
| 950 | |||
| 951 | 9 | static std::string render_window_bound(const detail::window_frame_bound_node& value) | |
| 952 | { | ||
| 953 |
5/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
|
9 | switch (value.kind) { |
| 954 | 2 | case detail::window_frame_bound_kind::unbounded_preceding: | |
| 955 |
1/1✓ Branch 1 taken 2 times.
|
4 | return "UNBOUNDED PRECEDING"; |
| 956 | 2 | case detail::window_frame_bound_kind::preceding: | |
| 957 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
|
2 | return std::to_string(value.offset) + " PRECEDING"; |
| 958 | 3 | case detail::window_frame_bound_kind::current_row: | |
| 959 |
1/1✓ Branch 1 taken 3 times.
|
6 | return "CURRENT ROW"; |
| 960 | 1 | case detail::window_frame_bound_kind::following: | |
| 961 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
|
1 | return std::to_string(value.offset) + " FOLLOWING"; |
| 962 | 1 | case detail::window_frame_bound_kind::unbounded_following: | |
| 963 |
1/1✓ Branch 1 taken 1 time.
|
2 | return "UNBOUNDED FOLLOWING"; |
| 964 | } | ||
| 965 | ✗ | throw render_error{"unknown window frame bound"}; | |
| 966 | } | ||
| 967 | |||
| 968 | 27 | static bool contains_distinct(const detail::expression_ptr& value) | |
| 969 | { | ||
| 970 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
|
27 | if (!value) return false; |
| 971 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 23 times.
|
27 | if (value->kind == detail::expression_kind::distinct) return true; |
| 972 |
2/2✓ Branch 6 taken 11 times.
✓ Branch 7 taken 19 times.
|
30 | for (const detail::expression_ptr& operand : value->operands) |
| 973 |
3/3✓ Branch 1 taken 11 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 7 times.
|
11 | if (contains_distinct(operand)) return true; |
| 974 | 19 | return false; | |
| 975 | } | ||
| 976 | |||
| 977 | 2 | std::string render_case(const detail::expression_node& value) | |
| 978 | { | ||
| 979 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
|
2 | if (value.operands.size() < 3 || value.operands.size() % 2 == 0) |
| 980 | ✗ | throw render_error{"CASE requires one or more WHEN branches and an ELSE expression"}; | |
| 981 | |||
| 982 |
1/1✓ Branch 1 taken 2 times.
|
2 | std::string sql{"CASE"}; |
| 983 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
|
6 | for (std::size_t index = 0; index + 1 < value.operands.size(); index += 2) { |
| 984 |
1/1✓ Branch 1 taken 4 times.
|
4 | sql += " WHEN "; |
| 985 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
|
4 | sql += render_expression(value.operands[index]); |
| 986 |
1/1✓ Branch 1 taken 4 times.
|
4 | sql += " THEN "; |
| 987 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 5 taken 4 times.
|
4 | sql += render_expression(value.operands[index + 1]); |
| 988 | } | ||
| 989 |
4/4✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 11 taken 2 times.
|
2 | sql += " ELSE " + render_expression(value.operands.back()) + " END"; |
| 990 | 2 | return sql; | |
| 991 | ✗ | } | |
| 992 | |||
| 993 | 118 | static int binary_precedence(const std::string& operation) | |
| 994 | { | ||
| 995 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 109 times.
|
118 | if (operation == "or") return 10; |
| 996 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 92 times.
|
109 | if (operation == "and") return 20; |
| 997 |
8/8✓ Branch 2 taken 32 times.
✓ Branch 3 taken 1 time.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 2 times.
✓ Branch 8 taken 29 times.
✓ Branch 9 taken 1 time.
✓ Branch 11 taken 14 times.
✓ Branch 12 taken 15 times.
|
125 | if (operation == "=" || operation == "!=" || operation == "<" || operation == "<=" || operation == ">" || |
| 998 |
10/10✓ Branch 0 taken 33 times.
✓ Branch 1 taken 59 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 1 time.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 10 times.
✓ Branch 11 taken 82 times.
✓ Branch 12 taken 10 times.
|
125 | operation == ">=" || operation == "like" || operation == "not_like") { |
| 999 | 82 | return 30; | |
| 1000 | } | ||
| 1001 | |||
| 1002 |
6/6✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 4 times.
|
10 | if (operation == "+" || operation == "-") return 40; |
| 1003 |
4/6✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
|
4 | if (operation == "*" || operation == "/") return 50; |
| 1004 | ✗ | throw render_error{"unknown binary expression operation"}; | |
| 1005 | } | ||
| 1006 | |||
| 1007 | 118 | static std::string binary_operator(const std::string& operation) | |
| 1008 | { | ||
| 1009 |
3/3✓ Branch 1 taken 17 times.
✓ Branch 2 taken 101 times.
✓ Branch 4 taken 17 times.
|
152 | if (operation == "and") return "AND"; |
| 1010 |
3/3✓ Branch 1 taken 9 times.
✓ Branch 2 taken 92 times.
✓ Branch 4 taken 9 times.
|
119 | if (operation == "or") return "OR"; |
| 1011 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 91 times.
✓ Branch 4 taken 1 time.
|
94 | if (operation == "!=") return "<>"; |
| 1012 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 90 times.
✓ Branch 4 taken 1 time.
|
93 | if (operation == "like") return "LIKE"; |
| 1013 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 89 times.
✓ Branch 4 taken 1 time.
|
92 | if (operation == "not_like") return "NOT LIKE"; |
| 1014 |
8/8✓ Branch 2 taken 28 times.
✓ Branch 3 taken 2 times.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 12 times.
✓ Branch 9 taken 15 times.
✓ Branch 11 taken 10 times.
✓ Branch 12 taken 2 times.
|
119 | if (operation == "=" || operation == "<" || operation == "<=" || operation == ">" || operation == ">=" || |
| 1015 |
10/12✓ Branch 0 taken 30 times.
✓ Branch 1 taken 59 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 1 time.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 3 times.
✓ Branch 12 taken 1 time.
✗ Branch 13 not taken.
✓ Branch 14 taken 89 times.
✗ Branch 15 not taken.
|
119 | operation == "+" || operation == "-" || operation == "*" || operation == "/") { |
| 1016 | 89 | return operation; | |
| 1017 | } | ||
| 1018 | |||
| 1019 | ✗ | throw render_error{"unknown binary expression operation"}; | |
| 1020 | } | ||
| 1021 | |||
| 1022 | 278 | std::string render_column(const detail::expression_node& value) const | |
| 1023 | { | ||
| 1024 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 278 times.
✗ Branch 5 not taken.
|
278 | if (value.text.empty()) throw render_error{"a column requires a name"}; |
| 1025 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 278 times.
|
278 | if (value.qualifier.empty()) return quote_identifier(value.text); |
| 1026 |
5/6✓ Branch 1 taken 1 time.
✓ Branch 2 taken 277 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 277 times.
|
278 | if (!m_merge_target_alias.empty() && value.qualifier == m_merge_target_qualifier) |
| 1027 |
4/4✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 10 taken 1 time.
|
1 | return quote_identifier(m_merge_target_alias) + "." + quote_identifier(value.text); |
| 1028 | 277 | return render_qualified_identifier(value.qualifier, value.text); | |
| 1029 | } | ||
| 1030 | |||
| 1031 | 20 | std::string render_inserted_column(const detail::expression_node& value) const | |
| 1032 | { | ||
| 1033 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 19 times.
|
20 | if (!m_rendering_conflict_assignments) |
| 1034 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"inserted column references are only valid in INSERT conflict assignments"}; |
| 1035 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 5 not taken.
|
19 | if (value.text.empty()) throw render_error{"an inserted column reference requires a name"}; |
| 1036 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 18 times.
|
19 | if (value.qualifier != m_inserted_target_qualifier) |
| 1037 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"inserted values must reference columns of the inserted table"}; |
| 1038 | |||
| 1039 |
4/5✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
|
18 | switch (m_options.inserted_row_references) { |
| 1040 | 8 | case inserted_row_reference_style::excluded_table: | |
| 1041 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
|
8 | return "EXCLUDED." + quote_identifier(value.text); |
| 1042 | 2 | case inserted_row_reference_style::values_row_alias: | |
| 1043 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (m_inserted_row_alias.empty()) |
| 1044 | ✗ | throw render_error{"the selected proposed-row reference style requires a VALUES row alias"}; | |
| 1045 |
4/4✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
|
2 | return quote_identifier(m_inserted_row_alias) + "." + quote_identifier(value.text); |
| 1046 | 7 | case inserted_row_reference_style::merge_source: | |
| 1047 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 5 not taken.
|
7 | if (m_inserted_row_alias.empty()) throw render_error{"a generated MERGE source alias is unavailable"}; |
| 1048 |
4/4✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
|
7 | return quote_identifier(m_inserted_row_alias) + "." + quote_identifier(value.text); |
| 1049 | 1 | case inserted_row_reference_style::unsupported: | |
| 1050 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"inserted column references are not supported by the selected SQL options"}; |
| 1051 | } | ||
| 1052 | ✗ | throw render_error{"unknown proposed-row reference style"}; | |
| 1053 | } | ||
| 1054 | |||
| 1055 | 121 | std::string render_relation(const detail::relation_ptr& value) | |
| 1056 | { | ||
| 1057 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 5 not taken.
|
121 | if (!value) throw render_error{"cannot render an empty relation node"}; |
| 1058 |
3/3✓ Branch 1 taken 16 times.
✓ Branch 2 taken 105 times.
✓ Branch 5 taken 14 times.
|
121 | if (value->kind == detail::relation_kind::join) return render_join(*value); |
| 1059 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 103 times.
|
105 | if (value->kind == detail::relation_kind::subquery) { |
| 1060 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 6 not taken.
|
2 | if (!value->subquery) throw render_error{"a derived relation requires a query"}; |
| 1061 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 6 not taken.
|
2 | if (value->alias.empty()) throw render_error{"a derived relation requires an alias"}; |
| 1062 |
5/5✓ Branch 2 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 13 taken 2 times.
✓ Branch 16 taken 2 times.
|
2 | return "(" + render_query_node(*value->subquery) + ")" + render_relation_alias(value->alias); |
| 1063 | } | ||
| 1064 | |||
| 1065 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 99 times.
|
103 | if (value->kind == detail::relation_kind::cte) { |
| 1066 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 6 not taken.
|
4 | if (value->name.empty()) throw render_error{"a CTE relation requires a name"}; |
| 1067 |
1/1✓ Branch 2 taken 4 times.
|
4 | std::string sql = quote_identifier(value->name); |
| 1068 |
1/4✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
|
4 | if (!value->alias.empty()) sql += render_relation_alias(value->alias); |
| 1069 | 4 | return sql; | |
| 1070 | 4 | } | |
| 1071 | |||
| 1072 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 5 not taken.
|
99 | if (value->kind != detail::relation_kind::table) throw render_error{"this relation kind is not implemented yet"}; |
| 1073 |
1/3✗ Branch 2 not taken.
✓ Branch 3 taken 99 times.
✗ Branch 6 not taken.
|
99 | if (value->name.empty()) throw render_error{"a table requires a name"}; |
| 1074 | |||
| 1075 |
1/1✓ Branch 3 taken 99 times.
|
99 | std::string sql = render_qualified_identifier(value->qualifiers, value->name); |
| 1076 |
4/4✓ Branch 2 taken 20 times.
✓ Branch 3 taken 79 times.
✓ Branch 6 taken 20 times.
✓ Branch 9 taken 20 times.
|
99 | if (!value->alias.empty()) sql += render_relation_alias(value->alias); |
| 1077 | 99 | return sql; | |
| 1078 | 99 | } | |
| 1079 | |||
| 1080 | 16 | std::string render_join(const detail::relation_node& value) | |
| 1081 | { | ||
| 1082 |
3/7✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 10 not taken.
|
16 | if (!value.left || !value.right) throw render_error{"a join requires left and right relations"}; |
| 1083 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 15 times.
|
19 | if (value.join_type == detail::join_kind::right && |
| 1084 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | !has_capability(m_options.capabilities, sql_capability::right_join)) { |
| 1085 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"RIGHT JOIN is not supported by the selected SQL options"}; |
| 1086 | } | ||
| 1087 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 14 times.
|
18 | if (value.join_type == detail::join_kind::full && |
| 1088 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | !has_capability(m_options.capabilities, sql_capability::full_join)) |
| 1089 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"FULL JOIN is not supported by the selected SQL options"}; |
| 1090 | |||
| 1091 | 14 | std::string sql = render_relation(value.left); | |
| 1092 |
4/4✓ Branch 1 taken 14 times.
✓ Branch 4 taken 14 times.
✓ Branch 7 taken 14 times.
✓ Branch 10 taken 14 times.
|
14 | sql += " " + join_keyword(value.join_type) + " "; |
| 1093 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 13 times.
|
14 | if (value.right->kind == detail::relation_kind::join) |
| 1094 |
4/4✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 10 taken 1 time.
|
1 | sql += "(" + render_relation(value.right) + ")"; |
| 1095 | else | ||
| 1096 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
|
13 | sql += render_relation(value.right); |
| 1097 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
|
14 | if (value.join_type == detail::join_kind::cross) { |
| 1098 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 5 not taken.
|
3 | if (value.condition) throw render_error{"CROSS JOIN cannot have an ON condition"}; |
| 1099 | 3 | return sql; | |
| 1100 | } | ||
| 1101 | |||
| 1102 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 5 not taken.
|
11 | if (!value.condition) throw render_error{"this join requires an ON condition"}; |
| 1103 |
1/1✓ Branch 1 taken 11 times.
|
11 | sql += " ON "; |
| 1104 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 4 taken 11 times.
|
11 | sql += render_expression(value.condition); |
| 1105 | 11 | return sql; | |
| 1106 | ✗ | } | |
| 1107 | |||
| 1108 | 14 | static std::string join_keyword(detail::join_kind kind) | |
| 1109 | { | ||
| 1110 |
5/6✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
14 | switch (kind) { |
| 1111 | 4 | case detail::join_kind::inner: | |
| 1112 |
1/1✓ Branch 1 taken 4 times.
|
8 | return "INNER JOIN"; |
| 1113 | 3 | case detail::join_kind::left: | |
| 1114 |
1/1✓ Branch 1 taken 3 times.
|
6 | return "LEFT JOIN"; |
| 1115 | 2 | case detail::join_kind::right: | |
| 1116 |
1/1✓ Branch 1 taken 2 times.
|
4 | return "RIGHT JOIN"; |
| 1117 | 2 | case detail::join_kind::full: | |
| 1118 |
1/1✓ Branch 1 taken 2 times.
|
4 | return "FULL JOIN"; |
| 1119 | 3 | case detail::join_kind::cross: | |
| 1120 |
1/1✓ Branch 1 taken 3 times.
|
6 | return "CROSS JOIN"; |
| 1121 | } | ||
| 1122 | ✗ | throw render_error{"unknown join kind"}; | |
| 1123 | } | ||
| 1124 | |||
| 1125 | 1053 | std::string quote_identifier(const std::string& identifier) const | |
| 1126 | { | ||
| 1127 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 1053 times.
✗ Branch 5 not taken.
|
1053 | if (identifier.empty()) throw render_error{"cannot quote an empty identifier"}; |
| 1128 |
2/2✓ Branch 0 taken 77 times.
✓ Branch 1 taken 976 times.
|
1053 | if (m_options.identifier_quotes == identifier_quote_style::brackets) { |
| 1129 |
1/1✓ Branch 1 taken 77 times.
|
77 | std::string result{"["}; |
| 1130 |
2/2✓ Branch 5 taken 513 times.
✓ Branch 6 taken 77 times.
|
590 | for (const char character : identifier) { |
| 1131 |
1/1✓ Branch 1 taken 513 times.
|
513 | result += character; |
| 1132 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 513 times.
✗ Branch 3 not taken.
|
513 | if (character == ']') result += ']'; |
| 1133 | } | ||
| 1134 |
1/1✓ Branch 1 taken 77 times.
|
77 | return result + "]"; |
| 1135 | 77 | } | |
| 1136 | |||
| 1137 |
2/2✓ Branch 0 taken 76 times.
✓ Branch 1 taken 900 times.
|
976 | const char quote = m_options.identifier_quotes == identifier_quote_style::backtick ? '`' : '"'; |
| 1138 |
1/1✓ Branch 1 taken 976 times.
|
976 | std::string result(1, quote); |
| 1139 |
2/2✓ Branch 5 taken 4731 times.
✓ Branch 6 taken 976 times.
|
5707 | for (const char character : identifier) { |
| 1140 |
1/1✓ Branch 1 taken 4731 times.
|
4731 | result += character; |
| 1141 |
3/3✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4728 times.
✓ Branch 3 taken 3 times.
|
4731 | if (character == quote) result += quote; |
| 1142 | } | ||
| 1143 |
1/1✓ Branch 1 taken 976 times.
|
976 | result += quote; |
| 1144 | 976 | return result; | |
| 1145 | 976 | } | |
| 1146 | |||
| 1147 | 13 | static std::string render_unquoted_identifier(const std::string& identifier) | |
| 1148 | { | ||
| 1149 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 5 not taken.
|
13 | if (identifier.empty()) throw render_error{"cannot render an empty unquoted identifier"}; |
| 1150 |
2/2✓ Branch 5 taken 71 times.
✓ Branch 6 taken 12 times.
|
83 | for (const char character : identifier) { |
| 1151 | 71 | const unsigned char value = static_cast<unsigned char>(character); | |
| 1152 |
7/8✓ Branch 0 taken 3 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
|
71 | if (!std::isalnum(value) && character != '_' && character != '$' && character != '#') |
| 1153 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"an unquoted identifier contains an unsupported character"}; |
| 1154 | } | ||
| 1155 | 12 | return identifier; | |
| 1156 | } | ||
| 1157 | |||
| 1158 | 36 | std::string render_relation_alias(const std::string& alias) const | |
| 1159 | { | ||
| 1160 |
3/3✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 36 times.
|
36 | const std::string prefix = m_options.relation_aliases == relation_alias_style::with_as ? " AS " : " "; |
| 1161 |
2/2✓ Branch 1 taken 36 times.
✓ Branch 4 taken 36 times.
|
72 | return prefix + quote_identifier(alias); |
| 1162 | 36 | } | |
| 1163 | |||
| 1164 | 454 | std::string render_qualified_identifier(const std::vector<std::string>& qualifiers, | |
| 1165 | const std::string& identifier) const | ||
| 1166 | { | ||
| 1167 | 454 | std::string sql; | |
| 1168 |
2/2✓ Branch 4 taken 288 times.
✓ Branch 5 taken 454 times.
|
742 | for (const std::string& qualifier : qualifiers) { |
| 1169 |
3/3✓ Branch 1 taken 4 times.
✓ Branch 2 taken 284 times.
✓ Branch 4 taken 4 times.
|
288 | if (!sql.empty()) sql += "."; |
| 1170 |
2/2✓ Branch 1 taken 288 times.
✓ Branch 4 taken 288 times.
|
288 | sql += quote_identifier(qualifier); |
| 1171 | } | ||
| 1172 |
3/3✓ Branch 1 taken 284 times.
✓ Branch 2 taken 170 times.
✓ Branch 4 taken 284 times.
|
454 | if (!sql.empty()) sql += "."; |
| 1173 |
2/2✓ Branch 1 taken 454 times.
✓ Branch 4 taken 454 times.
|
908 | return sql + quote_identifier(identifier); |
| 1174 | 454 | } | |
| 1175 | |||
| 1176 | 15 | static bool contains_inserted_row(const detail::expression_ptr& value) | |
| 1177 | { | ||
| 1178 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
|
15 | if (!value) return false; |
| 1179 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 6 times.
|
15 | if (value->kind == detail::expression_kind::inserted_column) return true; |
| 1180 |
1/2✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
|
6 | for (const detail::expression_ptr& operand : value->operands) |
| 1181 | ✗ | if (contains_inserted_row(operand)) return true; | |
| 1182 | 6 | return false; | |
| 1183 | } | ||
| 1184 | |||
| 1185 | 45 | static std::vector<std::string> relation_qualifier(const detail::relation_ptr& value) | |
| 1186 | { | ||
| 1187 |
3/6✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 45 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 45 times.
|
45 | if (!value || value->kind != detail::relation_kind::table) return {}; |
| 1188 |
1/7✗ Branch 2 not taken.
✓ Branch 3 taken 45 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
45 | if (!value->alias.empty()) return {value->alias}; |
| 1189 | |||
| 1190 |
1/1✓ Branch 2 taken 45 times.
|
45 | std::vector<std::string> qualifier = value->qualifiers; |
| 1191 |
1/1✓ Branch 2 taken 45 times.
|
45 | qualifier.push_back(value->name); |
| 1192 | 45 | return qualifier; | |
| 1193 | 45 | } | |
| 1194 | |||
| 1195 | 4 | static std::string make_inserted_row_alias(const detail::relation_ptr& target) | |
| 1196 | { | ||
| 1197 |
1/1✓ Branch 1 taken 4 times.
|
4 | std::string alias{"_sqlon_inserted"}; |
| 1198 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (!target) return alias; |
| 1199 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 4 times.
|
5 | while (equal_ascii_case_insensitive(alias, target->name)) |
| 1200 |
1/1✓ Branch 1 taken 1 time.
|
1 | alias += "_"; |
| 1201 | 4 | return alias; | |
| 1202 | ✗ | } | |
| 1203 | |||
| 1204 | 5 | static bool equal_ascii_case_insensitive(const std::string& left, const std::string& right) | |
| 1205 | { | ||
| 1206 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 time.
|
5 | if (left.size() != right.size()) return false; |
| 1207 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 1 time.
|
16 | for (std::size_t index = 0; index < left.size(); ++index) { |
| 1208 | 15 | const unsigned char left_character = static_cast<unsigned char>(left[index]); | |
| 1209 | 15 | const unsigned char right_character = static_cast<unsigned char>(right[index]); | |
| 1210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (std::tolower(left_character) != std::tolower(right_character)) return false; |
| 1211 | } | ||
| 1212 | 1 | return true; | |
| 1213 | } | ||
| 1214 | |||
| 1215 | 49 | static bool conflict_uses_inserted_row(const detail::conflict_clause_node& value) | |
| 1216 | { | ||
| 1217 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 45 times.
|
49 | if (value.action == detail::conflict_clause_node::kind::update_inserted_values) return true; |
| 1218 |
2/2✓ Branch 5 taken 15 times.
✓ Branch 6 taken 36 times.
|
51 | for (const detail::assignment_node& assignment : value.assignments) |
| 1219 |
3/3✓ Branch 1 taken 15 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 6 times.
|
15 | if (contains_inserted_row(assignment.value)) return true; |
| 1220 | 36 | return false; | |
| 1221 | } | ||
| 1222 | |||
| 1223 | 270 | std::string render_parameter(const detail::expression_node& value) | |
| 1224 | { | ||
| 1225 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 269 times.
|
270 | if (m_mode == renderer_mode::prepared) |
| 1226 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a prepared query definition must use parameter_slot values instead of bound values"}; |
| 1227 | |||
| 1228 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 269 times.
✗ Branch 5 not taken.
|
269 | if (!value.bound_parameter) throw render_error{"a bound parameter has no value"}; |
| 1229 | |||
| 1230 |
6/6✓ Branch 1 taken 25 times.
✓ Branch 2 taken 244 times.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 20 times.
✓ Branch 7 taken 249 times.
|
269 | if (!value.text.empty() && supports_parameter_reuse()) { |
| 1231 |
1/1✓ Branch 1 taken 20 times.
|
20 | const auto existing = m_named_parameter_positions.find(value.text); |
| 1232 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 3 taken 14 times.
|
20 | if (existing != m_named_parameter_positions.end()) { |
| 1233 | 6 | const parameter_storage& existing_value = m_result.parameters[existing->second].value.value(); | |
| 1234 |
3/3✓ Branch 3 taken 6 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 5 times.
|
6 | if (existing_value != value.bound_parameter->value()) |
| 1235 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a named parameter is used with conflicting values"}; |
| 1236 | |||
| 1237 |
1/1✓ Branch 4 taken 5 times.
|
5 | return *m_result.parameters[existing->second].placeholder; |
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | 263 | const std::size_t position = m_result.parameters.size(); | |
| 1242 |
1/1✓ Branch 1 taken 260 times.
|
263 | std::string rendered_placeholder = placeholder(position, value.text); |
| 1243 |
2/2✓ Branch 1 taken 242 times.
✓ Branch 2 taken 18 times.
|
520 | m_result.parameters.push_back({value.text.empty() ? std::nullopt : std::optional<std::string>{value.text}, |
| 1244 | *value.bound_parameter, rendered_placeholder}); | ||
| 1245 |
7/7✓ Branch 1 taken 18 times.
✓ Branch 2 taken 242 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 13 times.
✓ Branch 7 taken 247 times.
✓ Branch 9 taken 13 times.
|
260 | if (!value.text.empty() && supports_parameter_reuse()) m_named_parameter_positions.emplace(value.text, position); |
| 1246 | |||
| 1247 | 260 | return rendered_placeholder; | |
| 1248 | 520 | } | |
| 1249 | |||
| 1250 | 34 | std::string render_parameter_slot(const detail::expression_node& value) | |
| 1251 | { | ||
| 1252 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 4 not taken.
|
34 | if (m_mode != renderer_mode::prepared) throw render_error{"an unbound parameter slot requires prepare()"}; |
| 1253 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 5 not taken.
|
34 | if (!value.parameter_slot) throw render_error{"a prepared parameter slot has no metadata"}; |
| 1254 | |||
| 1255 | 34 | const detail::parameter_slot_node& slot = *value.parameter_slot; | |
| 1256 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 5 not taken.
|
34 | if (slot.name.empty()) throw render_error{"a prepared parameter requires a non-empty name"}; |
| 1257 | |||
| 1258 | 34 | std::size_t logical_index{}; | |
| 1259 | const std::unordered_map<std::size_t, std::size_t>::const_iterator existing_slot = | ||
| 1260 |
1/1✓ Branch 1 taken 34 times.
|
34 | m_prepared_parameter_indices.find(slot.identity); |
| 1261 |
2/2✓ Branch 2 taken 29 times.
✓ Branch 3 taken 5 times.
|
34 | if (existing_slot == m_prepared_parameter_indices.end()) { |
| 1262 | const std::unordered_map<std::string, std::size_t>::const_iterator existing_name = | ||
| 1263 |
1/1✓ Branch 1 taken 29 times.
|
29 | m_prepared_parameter_names.find(slot.name); |
| 1264 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 28 times.
|
29 | if (existing_name != m_prepared_parameter_names.end()) |
| 1265 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"prepared parameter names must be unique"}; |
| 1266 | |||
| 1267 | 28 | logical_index = m_prepared_parameters.size(); | |
| 1268 | 28 | m_prepared_parameters.push_back({slot.identity, slot.name, slot.kind, slot.nullable, slot.custom_type_id}); | |
| 1269 |
1/1✓ Branch 1 taken 28 times.
|
28 | m_prepared_parameter_indices.emplace(slot.identity, logical_index); |
| 1270 |
1/1✓ Branch 1 taken 28 times.
|
28 | m_prepared_parameter_names.emplace(slot.name, logical_index); |
| 1271 | } else { | ||
| 1272 | 5 | logical_index = existing_slot->second; | |
| 1273 | 5 | const prepared_parameter& existing = m_prepared_parameters[logical_index]; | |
| 1274 | 5 | const bool custom_types_match = existing.custom_type_id == slot.custom_type_id; | |
| 1275 |
4/8✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
|
10 | if (existing.name != slot.name || existing.kind != slot.kind || existing.nullable != slot.nullable || |
| 1276 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | !custom_types_match) |
| 1277 | ✗ | throw render_error{"a prepared parameter identity has conflicting metadata"}; | |
| 1278 | } | ||
| 1279 | |||
| 1280 |
2/2✓ Branch 1 taken 29 times.
✓ Branch 2 taken 4 times.
|
33 | if (supports_parameter_reuse()) { |
| 1281 | const std::unordered_map<std::size_t, std::size_t>::const_iterator existing_position = | ||
| 1282 |
1/1✓ Branch 1 taken 29 times.
|
29 | m_prepared_parameter_positions.find(slot.identity); |
| 1283 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 3 taken 26 times.
|
29 | if (existing_position != m_prepared_parameter_positions.end()) |
| 1284 |
1/1✓ Branch 4 taken 3 times.
|
3 | return *m_prepared_bindings[existing_position->second].placeholder; |
| 1285 | } | ||
| 1286 | |||
| 1287 | 30 | const std::size_t binding_position = m_prepared_bindings.size(); | |
| 1288 |
1/1✓ Branch 1 taken 30 times.
|
30 | std::string rendered_placeholder = placeholder(binding_position, slot.name); |
| 1289 |
1/1✓ Branch 1 taken 30 times.
|
30 | m_prepared_bindings.push_back({logical_index, rendered_placeholder}); |
| 1290 |
3/3✓ Branch 1 taken 26 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 26 times.
|
30 | if (supports_parameter_reuse()) m_prepared_parameter_positions.emplace(slot.identity, binding_position); |
| 1291 | 30 | return rendered_placeholder; | |
| 1292 | 88 | } | |
| 1293 | |||
| 1294 | 293 | std::string placeholder(std::size_t position, const std::string& logical_name) | |
| 1295 | { | ||
| 1296 |
4/4✓ Branch 0 taken 41 times.
✓ Branch 1 taken 187 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 1 time.
|
293 | switch (m_options.placeholders) { |
| 1297 | 41 | case placeholder_style::question_mark: | |
| 1298 |
1/1✓ Branch 1 taken 41 times.
|
82 | return "?"; |
| 1299 | 187 | case placeholder_style::numbered: | |
| 1300 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 186 times.
|
187 | if (m_options.numbered_parameter_prefix.empty()) |
| 1301 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a numbered parameter prefix cannot be empty"}; |
| 1302 |
2/2✓ Branch 1 taken 186 times.
✓ Branch 4 taken 186 times.
|
186 | return m_options.numbered_parameter_prefix + std::to_string(position + 1); |
| 1303 | 64 | case placeholder_style::named: { | |
| 1304 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 5 not taken.
|
64 | if (m_options.named_parameter_prefix.empty()) throw render_error{"a named parameter prefix cannot be empty"}; |
| 1305 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 48 times.
|
64 | if (!logical_name.empty()) { |
| 1306 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 15 times.
|
16 | if (!valid_named_placeholder(logical_name)) |
| 1307 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"a named placeholder requires an identifier-like parameter name"}; |
| 1308 |
3/3✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 12 times.
|
15 | if (supports_parameter_reuse()) return m_options.named_parameter_prefix + logical_name; |
| 1309 |
3/3✓ Branch 1 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 time.
|
3 | if (m_emitted_parameter_names.insert(logical_name).second) |
| 1310 |
1/1✓ Branch 1 taken 2 times.
|
2 | return m_options.named_parameter_prefix + logical_name; |
| 1311 | |||
| 1312 | 1 | std::size_t suffix = position + 1; | |
| 1313 | 1 | std::string emitted_name; | |
| 1314 | do | ||
| 1315 |
3/3✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
|
2 | emitted_name = logical_name + "_" + std::to_string(suffix++); |
| 1316 |
5/5✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
|
3 | while (m_reserved_parameter_names.count(emitted_name) != 0U || |
| 1317 |
2/3✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
|
1 | !m_emitted_parameter_names.insert(emitted_name).second); |
| 1318 |
1/1✓ Branch 1 taken 1 time.
|
1 | return m_options.named_parameter_prefix + emitted_name; |
| 1319 | 1 | } | |
| 1320 | |||
| 1321 | 48 | std::size_t suffix = position + 1; | |
| 1322 | 48 | std::string generated_name; | |
| 1323 | do | ||
| 1324 |
2/2✓ Branch 1 taken 49 times.
✓ Branch 4 taken 49 times.
|
49 | generated_name = "p" + std::to_string(suffix++); |
| 1325 |
3/3✓ Branch 1 taken 49 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 48 times.
|
49 | while (!m_reserved_parameter_names.insert(generated_name).second); |
| 1326 |
1/1✓ Branch 1 taken 48 times.
|
48 | return m_options.named_parameter_prefix + generated_name; |
| 1327 | 48 | } | |
| 1328 | } | ||
| 1329 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"unknown placeholder style"}; |
| 1330 | } | ||
| 1331 | |||
| 1332 | 16 | static bool valid_named_placeholder(const std::string& value) | |
| 1333 | { | ||
| 1334 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (value.empty()) return false; |
| 1335 | 16 | const unsigned char first = static_cast<unsigned char>(value.front()); | |
| 1336 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 16 times.
|
16 | if (std::isalpha(first) == 0 && value.front() != '_') return false; |
| 1337 |
2/2✓ Branch 5 taken 118 times.
✓ Branch 6 taken 15 times.
|
133 | for (const char character : value) { |
| 1338 | 118 | const unsigned char byte = static_cast<unsigned char>(character); | |
| 1339 |
4/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 9 times.
|
118 | if (std::isalnum(byte) == 0 && character != '_') return false; |
| 1340 | } | ||
| 1341 | 15 | return true; | |
| 1342 | } | ||
| 1343 | |||
| 1344 | 294 | void collect_parameter_names(const detail::query_node& value) | |
| 1345 | { | ||
| 1346 |
2/2✓ Branch 5 taken 276 times.
✓ Branch 6 taken 294 times.
|
570 | for (const detail::expression_ptr& expression : value.projection) |
| 1347 |
1/1✓ Branch 1 taken 276 times.
|
276 | collect_parameter_names(expression); |
| 1348 | 294 | collect_parameter_names(value.from); | |
| 1349 | 294 | collect_parameter_names(value.where); | |
| 1350 |
2/2✓ Branch 5 taken 7 times.
✓ Branch 6 taken 294 times.
|
301 | for (const detail::expression_ptr& expression : value.group_by) |
| 1351 |
1/1✓ Branch 1 taken 7 times.
|
7 | collect_parameter_names(expression); |
| 1352 | 294 | collect_parameter_names(value.having); | |
| 1353 |
2/2✓ Branch 5 taken 17 times.
✓ Branch 6 taken 294 times.
|
311 | for (const detail::order_by_node& order : value.order_by) |
| 1354 |
1/1✓ Branch 1 taken 17 times.
|
17 | collect_parameter_names(order.expression); |
| 1355 |
2/2✓ Branch 5 taken 18 times.
✓ Branch 6 taken 294 times.
|
312 | for (const detail::set_operation_node& operation : value.set_operations) |
| 1356 |
2/3✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 18 times.
|
18 | if (operation.query) collect_parameter_names(*operation.query); |
| 1357 |
2/2✓ Branch 5 taken 15 times.
✓ Branch 6 taken 294 times.
|
309 | for (const detail::common_table_expression_node& cte : value.ctes) |
| 1358 |
2/3✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 15 times.
|
15 | if (cte.query) collect_parameter_names(*cte.query); |
| 1359 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 289 times.
|
294 | if (value.insert_source) collect_parameter_names(*value.insert_source); |
| 1360 |
2/2✓ Branch 5 taken 65 times.
✓ Branch 6 taken 294 times.
|
359 | for (const std::vector<detail::expression_ptr>& row : value.values) |
| 1361 |
2/2✓ Branch 5 taken 124 times.
✓ Branch 6 taken 65 times.
|
189 | for (const detail::expression_ptr& expression : row) |
| 1362 |
1/1✓ Branch 1 taken 124 times.
|
124 | collect_parameter_names(expression); |
| 1363 |
2/2✓ Branch 5 taken 13 times.
✓ Branch 6 taken 294 times.
|
307 | for (const detail::assignment_node& assignment : value.assignments) |
| 1364 |
1/1✓ Branch 1 taken 13 times.
|
13 | collect_parameter_names(assignment.value); |
| 1365 |
2/2✓ Branch 5 taken 21 times.
✓ Branch 6 taken 294 times.
|
315 | for (const detail::assignment_node& assignment : value.conflict.assignments) |
| 1366 |
1/1✓ Branch 1 taken 21 times.
|
21 | collect_parameter_names(assignment.value); |
| 1367 |
2/2✓ Branch 5 taken 13 times.
✓ Branch 6 taken 294 times.
|
307 | for (const detail::expression_ptr& expression : value.returning) |
| 1368 |
1/1✓ Branch 1 taken 13 times.
|
13 | collect_parameter_names(expression); |
| 1369 | 294 | } | |
| 1370 | |||
| 1371 | 562 | void collect_parameter_names(const detail::relation_ptr& value) | |
| 1372 | { | ||
| 1373 |
2/2✓ Branch 1 taken 428 times.
✓ Branch 2 taken 134 times.
|
562 | if (!value) return; |
| 1374 | 134 | collect_parameter_names(value->left); | |
| 1375 | 134 | collect_parameter_names(value->right); | |
| 1376 | 134 | collect_parameter_names(value->condition); | |
| 1377 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 132 times.
|
134 | if (value->subquery) collect_parameter_names(*value->subquery); |
| 1378 | } | ||
| 1379 | |||
| 1380 | 1605 | void collect_parameter_names(const detail::expression_ptr& value) | |
| 1381 | { | ||
| 1382 |
2/2✓ Branch 1 taken 649 times.
✓ Branch 2 taken 956 times.
|
1605 | if (!value) return; |
| 1383 |
6/6✓ Branch 1 taken 653 times.
✓ Branch 2 taken 303 times.
✓ Branch 4 taken 34 times.
✓ Branch 5 taken 619 times.
✓ Branch 6 taken 25 times.
✓ Branch 7 taken 931 times.
|
1293 | if ((value->kind == detail::expression_kind::parameter || value->kind == detail::expression_kind::parameter_slot) && |
| 1384 |
2/2✓ Branch 2 taken 25 times.
✓ Branch 3 taken 312 times.
|
337 | !value->text.empty()) { |
| 1385 | 25 | m_reserved_parameter_names.insert(value->text); | |
| 1386 | } | ||
| 1387 |
2/2✓ Branch 6 taken 404 times.
✓ Branch 7 taken 956 times.
|
1360 | for (const detail::expression_ptr& operand : value->operands) |
| 1388 |
1/1✓ Branch 1 taken 404 times.
|
404 | collect_parameter_names(operand); |
| 1389 |
2/2✓ Branch 2 taken 17 times.
✓ Branch 3 taken 939 times.
|
956 | if (value->window) { |
| 1390 |
2/2✓ Branch 7 taken 4 times.
✓ Branch 8 taken 17 times.
|
21 | for (const detail::expression_ptr& expression : value->window->partition_by) |
| 1391 |
1/1✓ Branch 1 taken 4 times.
|
4 | collect_parameter_names(expression); |
| 1392 |
2/2✓ Branch 7 taken 4 times.
✓ Branch 8 taken 17 times.
|
21 | for (const detail::order_by_node& order : value->window->order_by) |
| 1393 |
1/1✓ Branch 1 taken 4 times.
|
4 | collect_parameter_names(order.expression); |
| 1394 | } | ||
| 1395 |
2/2✓ Branch 2 taken 8 times.
✓ Branch 3 taken 948 times.
|
956 | if (value->subquery) collect_parameter_names(*value->subquery); |
| 1396 | } | ||
| 1397 | |||
| 1398 | 121 | bool supports_parameter_reuse() const noexcept | |
| 1399 | { | ||
| 1400 |
3/4✓ Branch 0 taken 61 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
121 | switch (m_options.placeholders) { |
| 1401 | 61 | case placeholder_style::numbered: | |
| 1402 | 61 | return has_capability(m_options.capabilities, sql_capability::reusable_numbered_parameters); | |
| 1403 | 48 | case placeholder_style::named: | |
| 1404 | 48 | return has_capability(m_options.capabilities, sql_capability::reusable_named_parameters); | |
| 1405 | 12 | case placeholder_style::question_mark: | |
| 1406 | 12 | return false; | |
| 1407 | } | ||
| 1408 | ✗ | return false; | |
| 1409 | } | ||
| 1410 | |||
| 1411 | 41 | std::string render_literal(const detail::expression_node& value) const | |
| 1412 | { | ||
| 1413 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 41 times.
✗ Branch 5 not taken.
|
41 | if (!value.bound_parameter) throw render_error{"a SQL literal has no value"}; |
| 1414 | |||
| 1415 | 41 | return value.bound_parameter->visit_storage([this](const auto& literal_value) -> std::string { | |
| 1416 | using value_type = std::decay_t<decltype(literal_value)>; | ||
| 1417 | if constexpr (std::is_same_v<value_type, std::nullptr_t>) | ||
| 1418 |
1/1✓ Branch 1 taken 1 time.
|
2 | return "NULL"; |
| 1419 | else if constexpr (std::is_same_v<value_type, bool>) | ||
| 1420 | 2 | return m_options.boolean_literals == boolean_literal_style::integers ? (literal_value ? "1" : "0") | |
| 1421 |
5/7✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
|
4 | : (literal_value ? "TRUE" : "FALSE"); |
| 1422 | else if constexpr (std::is_integral_v<value_type>) | ||
| 1423 | 28 | return std::to_string(literal_value); | |
| 1424 | else if constexpr (std::is_floating_point_v<value_type>) { | ||
| 1425 |
3/3✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
|
2 | if (!std::isfinite(literal_value)) throw render_error{"non-finite SQL literals are not supported"}; |
| 1426 |
1/1✓ Branch 1 taken 1 time.
|
1 | std::ostringstream stream; |
| 1427 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
|
1 | stream.imbue(std::locale::classic()); |
| 1428 |
1/1✓ Branch 3 taken 1 time.
|
1 | stream << std::setprecision(std::numeric_limits<value_type>::max_digits10) << literal_value; |
| 1429 |
1/1✓ Branch 1 taken 1 time.
|
2 | return stream.str(); |
| 1430 | 1 | } else if constexpr (std::is_same_v<value_type, std::string>) { | |
| 1431 |
1/1✓ Branch 1 taken 6 times.
|
6 | std::string result{"'"}; |
| 1432 |
2/2✓ Branch 5 taken 36 times.
✓ Branch 6 taken 4 times.
|
40 | for (const char character : literal_value) { |
| 1433 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 23 times.
|
36 | if (m_options.string_literals == string_literal_style::backslash_escaped) { |
| 1434 |
9/9✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 5 times.
|
13 | switch (character) { |
| 1435 | 1 | case '\0': | |
| 1436 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\0"; |
| 1437 | 1 | break; | |
| 1438 | 1 | case '\b': | |
| 1439 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\b"; |
| 1440 | 1 | break; | |
| 1441 | 1 | case '\n': | |
| 1442 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\n"; |
| 1443 | 1 | break; | |
| 1444 | 1 | case '\r': | |
| 1445 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\r"; |
| 1446 | 1 | break; | |
| 1447 | 1 | case '\t': | |
| 1448 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\t"; |
| 1449 | 1 | break; | |
| 1450 | 1 | case '\x1a': | |
| 1451 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\Z"; |
| 1452 | 1 | break; | |
| 1453 | 1 | case '\\': | |
| 1454 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\\\"; |
| 1455 | 1 | break; | |
| 1456 | 1 | case '\'': | |
| 1457 |
1/1✓ Branch 1 taken 1 time.
|
1 | result += "\\'"; |
| 1458 | 1 | break; | |
| 1459 | 5 | default: | |
| 1460 |
1/1✓ Branch 1 taken 5 times.
|
5 | result += character; |
| 1461 | 5 | break; | |
| 1462 | } | ||
| 1463 | } else { | ||
| 1464 |
3/3✓ Branch 0 taken 1 time.
✓ Branch 1 taken 22 times.
✓ Branch 4 taken 1 time.
|
23 | if (character == '\0') throw render_error{"NUL bytes are not supported in standard SQL literals"}; |
| 1465 |
3/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
|
22 | if (character == '\\' && m_options.string_literals == string_literal_style::mode_independent) { |
| 1466 |
1/1✓ Branch 2 taken 1 time.
|
1 | throw render_error{"backslashes in mode-independent SQL literals require a bound parameter"}; |
| 1467 | } | ||
| 1468 |
1/1✓ Branch 1 taken 21 times.
|
21 | result += character; |
| 1469 |
3/3✓ Branch 0 taken 2 times.
✓ Branch 1 taken 19 times.
✓ Branch 3 taken 2 times.
|
21 | if (character == '\'') result += '\''; |
| 1470 | } | ||
| 1471 | } | ||
| 1472 |
1/1✓ Branch 1 taken 4 times.
|
4 | result += '\''; |
| 1473 | 8 | return result; | |
| 1474 | 6 | } else | |
| 1475 |
2/2std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > sqlon::(anonymous namespace)::renderer::render_literal(sqlon::detail::expression_node const&) const::{lambda(auto:1 const&)#1}::operator()<sqlon::custom_parameter_value>(sqlon::custom_parameter_value const&) const:
✓ Branch 2 taken 1 time.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > sqlon::(anonymous namespace)::renderer::render_literal(sqlon::detail::expression_node const&) const::{lambda(auto:1 const&)#1}::operator()<std::vector<std::byte, std::allocator<std::byte> > >(std::vector<std::byte, std::allocator<std::byte> > const&) const:
✓ Branch 2 taken 1 time.
|
2 | throw render_error{"binary and custom SQL literals are not supported"}; |
| 1476 | }); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | 6 | std::string render_keyword(const detail::expression_node& value) const | |
| 1480 | { | ||
| 1481 |
3/3✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 2 times.
|
10 | if (value.text == "current_timestamp") return "CURRENT_TIMESTAMP"; |
| 1482 |
3/3✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 3 times.
|
10 | if (value.text == "null") return "NULL"; |
| 1483 |
2/3✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 time.
|
1 | if (value.text == "default") throw render_error{"DEFAULT is only valid as an INSERT or assignment value"}; |
| 1484 | ✗ | throw render_error{"unknown SQL keyword expression"}; | |
| 1485 | } | ||
| 1486 | |||
| 1487 | const sql_options& m_options; | ||
| 1488 | renderer_mode m_mode; | ||
| 1489 | bool m_rendering_conflict_assignments{}; | ||
| 1490 | std::string m_inserted_row_alias; | ||
| 1491 | std::vector<std::string> m_inserted_target_qualifier; | ||
| 1492 | std::string m_merge_target_alias; | ||
| 1493 | std::vector<std::string> m_merge_target_qualifier; | ||
| 1494 | rendered_query m_result; | ||
| 1495 | std::unordered_map<std::string, std::size_t> m_named_parameter_positions; | ||
| 1496 | std::unordered_set<std::string> m_reserved_parameter_names; | ||
| 1497 | std::unordered_set<std::string> m_emitted_parameter_names; | ||
| 1498 | std::vector<prepared_parameter> m_prepared_parameters; | ||
| 1499 | std::vector<prepared_binding> m_prepared_bindings; | ||
| 1500 | std::unordered_map<std::size_t, std::size_t> m_prepared_parameter_indices; | ||
| 1501 | std::unordered_map<std::string, std::size_t> m_prepared_parameter_names; | ||
| 1502 | std::unordered_map<std::size_t, std::size_t> m_prepared_parameter_positions; | ||
| 1503 | }; | ||
| 1504 | |||
| 1505 | } // namespace | ||
| 1506 | |||
| 1507 | 224 | rendered_query render(const query& value, const sql_options& options) | |
| 1508 | { | ||
| 1509 |
1/1✓ Branch 2 taken 158 times.
|
224 | return renderer{options, renderer_mode::immediate}.render_query(value); |
| 1510 | } | ||
| 1511 | |||
| 1512 | 22 | prepared_query prepare(const query& value, const sql_options& options) | |
| 1513 | { | ||
| 1514 |
1/1✓ Branch 2 taken 20 times.
|
22 | return renderer{options, renderer_mode::prepared}.render_prepared_query(value); |
| 1515 | } | ||
| 1516 | |||
| 1517 | } // namespace sqlon | ||
| 1518 |