| 1 | #include "duckdb/parser/tableref/pivotref.hpp" |
| 2 | |
| 3 | #include "duckdb/common/limits.hpp" |
| 4 | #include "duckdb/common/field_writer.hpp" |
| 5 | #include "duckdb/common/serializer/format_serializer.hpp" |
| 6 | #include "duckdb/common/serializer/format_deserializer.hpp" |
| 7 | |
| 8 | namespace duckdb { |
| 9 | |
| 10 | //===--------------------------------------------------------------------===// |
| 11 | // PivotColumn |
| 12 | //===--------------------------------------------------------------------===// |
| 13 | string PivotColumn::ToString() const { |
| 14 | string result; |
| 15 | if (!unpivot_names.empty()) { |
| 16 | D_ASSERT(pivot_expressions.empty()); |
| 17 | // unpivot |
| 18 | if (unpivot_names.size() == 1) { |
| 19 | result += KeywordHelper::WriteOptionallyQuoted(text: unpivot_names[0]); |
| 20 | } else { |
| 21 | result += "(" ; |
| 22 | for (idx_t n = 0; n < unpivot_names.size(); n++) { |
| 23 | if (n > 0) { |
| 24 | result += ", " ; |
| 25 | } |
| 26 | result += KeywordHelper::WriteOptionallyQuoted(text: unpivot_names[n]); |
| 27 | } |
| 28 | result += ")" ; |
| 29 | } |
| 30 | } else if (!pivot_expressions.empty()) { |
| 31 | // pivot |
| 32 | result += "(" ; |
| 33 | for (idx_t n = 0; n < pivot_expressions.size(); n++) { |
| 34 | if (n > 0) { |
| 35 | result += ", " ; |
| 36 | } |
| 37 | result += pivot_expressions[n]->ToString(); |
| 38 | } |
| 39 | result += ")" ; |
| 40 | } |
| 41 | result += " IN " ; |
| 42 | if (pivot_enum.empty()) { |
| 43 | result += "(" ; |
| 44 | for (idx_t e = 0; e < entries.size(); e++) { |
| 45 | auto &entry = entries[e]; |
| 46 | if (e > 0) { |
| 47 | result += ", " ; |
| 48 | } |
| 49 | if (entry.star_expr) { |
| 50 | D_ASSERT(entry.values.empty()); |
| 51 | result += entry.star_expr->ToString(); |
| 52 | } else if (entry.values.size() == 1) { |
| 53 | result += entry.values[0].ToSQLString(); |
| 54 | } else { |
| 55 | result += "(" ; |
| 56 | for (idx_t v = 0; v < entry.values.size(); v++) { |
| 57 | if (v > 0) { |
| 58 | result += ", " ; |
| 59 | } |
| 60 | result += entry.values[v].ToSQLString(); |
| 61 | } |
| 62 | result += ")" ; |
| 63 | } |
| 64 | if (!entry.alias.empty()) { |
| 65 | result += " AS " + KeywordHelper::WriteOptionallyQuoted(text: entry.alias); |
| 66 | } |
| 67 | } |
| 68 | result += ")" ; |
| 69 | } else { |
| 70 | result += KeywordHelper::WriteOptionallyQuoted(text: pivot_enum); |
| 71 | } |
| 72 | return result; |
| 73 | } |
| 74 | |
| 75 | bool PivotColumnEntry::Equals(const PivotColumnEntry &other) const { |
| 76 | if (alias != other.alias) { |
| 77 | return false; |
| 78 | } |
| 79 | if (values.size() != other.values.size()) { |
| 80 | return false; |
| 81 | } |
| 82 | for (idx_t i = 0; i < values.size(); i++) { |
| 83 | if (!Value::NotDistinctFrom(lvalue: values[i], rvalue: other.values[i])) { |
| 84 | return false; |
| 85 | } |
| 86 | } |
| 87 | return true; |
| 88 | } |
| 89 | |
| 90 | bool PivotColumn::Equals(const PivotColumn &other) const { |
| 91 | if (!ExpressionUtil::ListEquals(a: pivot_expressions, b: other.pivot_expressions)) { |
| 92 | return false; |
| 93 | } |
| 94 | if (other.unpivot_names != unpivot_names) { |
| 95 | return false; |
| 96 | } |
| 97 | if (other.pivot_enum != pivot_enum) { |
| 98 | return false; |
| 99 | } |
| 100 | if (other.entries.size() != entries.size()) { |
| 101 | return false; |
| 102 | } |
| 103 | for (idx_t i = 0; i < entries.size(); i++) { |
| 104 | if (!entries[i].Equals(other: other.entries[i])) { |
| 105 | return false; |
| 106 | } |
| 107 | } |
| 108 | return true; |
| 109 | } |
| 110 | |
| 111 | PivotColumn PivotColumn::Copy() const { |
| 112 | PivotColumn result; |
| 113 | for (auto &expr : pivot_expressions) { |
| 114 | result.pivot_expressions.push_back(x: expr->Copy()); |
| 115 | } |
| 116 | result.unpivot_names = unpivot_names; |
| 117 | for (auto &entry : entries) { |
| 118 | result.entries.push_back(x: entry.Copy()); |
| 119 | } |
| 120 | result.pivot_enum = pivot_enum; |
| 121 | return result; |
| 122 | } |
| 123 | |
| 124 | void PivotColumn::Serialize(Serializer &serializer) const { |
| 125 | FieldWriter writer(serializer); |
| 126 | writer.WriteSerializableList(elements: pivot_expressions); |
| 127 | writer.WriteList<string>(elements: unpivot_names); |
| 128 | writer.WriteRegularSerializableList(elements: entries); |
| 129 | writer.WriteString(val: pivot_enum); |
| 130 | writer.Finalize(); |
| 131 | } |
| 132 | |
| 133 | void PivotColumn::FormatSerialize(FormatSerializer &serializer) const { |
| 134 | serializer.WriteProperty(tag: "pivot_expressions" , value: pivot_expressions); |
| 135 | serializer.WriteProperty(tag: "unpivot_names" , value: unpivot_names); |
| 136 | serializer.WriteProperty(tag: "entries" , value: entries); |
| 137 | serializer.WriteProperty(tag: "pivot_enum" , value: pivot_enum); |
| 138 | } |
| 139 | |
| 140 | PivotColumn PivotColumn::Deserialize(Deserializer &source) { |
| 141 | PivotColumn result; |
| 142 | FieldReader reader(source); |
| 143 | result.pivot_expressions = reader.ReadRequiredSerializableList<ParsedExpression>(); |
| 144 | result.unpivot_names = reader.ReadRequiredList<string>(); |
| 145 | result.entries = reader.ReadRequiredSerializableList<PivotColumnEntry, PivotColumnEntry>(); |
| 146 | result.pivot_enum = reader.ReadRequired<string>(); |
| 147 | reader.Finalize(); |
| 148 | return result; |
| 149 | } |
| 150 | |
| 151 | PivotColumn PivotColumn::FormatDeserialize(FormatDeserializer &source) { |
| 152 | PivotColumn result; |
| 153 | source.ReadProperty(tag: "pivot_expressions" , ret&: result.pivot_expressions); |
| 154 | source.ReadProperty(tag: "unpivot_names" , ret&: result.unpivot_names); |
| 155 | source.ReadProperty(tag: "entries" , ret&: result.entries); |
| 156 | source.ReadProperty(tag: "pivot_enum" , ret&: result.pivot_enum); |
| 157 | return result; |
| 158 | } |
| 159 | |
| 160 | //===--------------------------------------------------------------------===// |
| 161 | // PivotColumnEntry |
| 162 | //===--------------------------------------------------------------------===// |
| 163 | PivotColumnEntry PivotColumnEntry::Copy() const { |
| 164 | PivotColumnEntry result; |
| 165 | result.values = values; |
| 166 | result.star_expr = star_expr ? star_expr->Copy() : nullptr; |
| 167 | result.alias = alias; |
| 168 | return result; |
| 169 | } |
| 170 | |
| 171 | void PivotColumnEntry::Serialize(Serializer &serializer) const { |
| 172 | FieldWriter writer(serializer); |
| 173 | writer.WriteRegularSerializableList(elements: values); |
| 174 | writer.WriteOptional(element: star_expr); |
| 175 | writer.WriteString(val: alias); |
| 176 | writer.Finalize(); |
| 177 | } |
| 178 | |
| 179 | void PivotColumnEntry::FormatSerialize(FormatSerializer &serializer) const { |
| 180 | serializer.WriteProperty(tag: "values" , value: values); |
| 181 | serializer.WriteOptionalProperty(tag: "star_expr" , ptr: star_expr); |
| 182 | serializer.WriteProperty(tag: "alias" , value: alias); |
| 183 | } |
| 184 | |
| 185 | PivotColumnEntry PivotColumnEntry::Deserialize(Deserializer &source) { |
| 186 | PivotColumnEntry result; |
| 187 | FieldReader reader(source); |
| 188 | result.values = reader.ReadRequiredSerializableList<Value, Value>(); |
| 189 | result.star_expr = reader.ReadOptional<ParsedExpression>(default_value: nullptr); |
| 190 | result.alias = reader.ReadRequired<string>(); |
| 191 | reader.Finalize(); |
| 192 | return result; |
| 193 | } |
| 194 | |
| 195 | PivotColumnEntry PivotColumnEntry::FormatDeserialize(FormatDeserializer &source) { |
| 196 | PivotColumnEntry result; |
| 197 | source.ReadProperty(tag: "values" , ret&: result.values); |
| 198 | source.ReadOptionalProperty(tag: "star_expr" , ret&: result.star_expr); |
| 199 | source.ReadProperty(tag: "alias" , ret&: result.alias); |
| 200 | return result; |
| 201 | } |
| 202 | |
| 203 | //===--------------------------------------------------------------------===// |
| 204 | // PivotRef |
| 205 | //===--------------------------------------------------------------------===// |
| 206 | string PivotRef::ToString() const { |
| 207 | string result; |
| 208 | result = source->ToString(); |
| 209 | if (!aggregates.empty()) { |
| 210 | // pivot |
| 211 | result += " PIVOT (" ; |
| 212 | for (idx_t aggr_idx = 0; aggr_idx < aggregates.size(); aggr_idx++) { |
| 213 | if (aggr_idx > 0) { |
| 214 | result += ", " ; |
| 215 | } |
| 216 | result += aggregates[aggr_idx]->ToString(); |
| 217 | if (!aggregates[aggr_idx]->alias.empty()) { |
| 218 | result += " AS " + KeywordHelper::WriteOptionallyQuoted(text: aggregates[aggr_idx]->alias); |
| 219 | } |
| 220 | } |
| 221 | } else { |
| 222 | // unpivot |
| 223 | result += " UNPIVOT " ; |
| 224 | if (include_nulls) { |
| 225 | result += "INCLUDE NULLS " ; |
| 226 | } |
| 227 | result += "(" ; |
| 228 | if (unpivot_names.size() == 1) { |
| 229 | result += KeywordHelper::WriteOptionallyQuoted(text: unpivot_names[0]); |
| 230 | } else { |
| 231 | result += "(" ; |
| 232 | for (idx_t n = 0; n < unpivot_names.size(); n++) { |
| 233 | if (n > 0) { |
| 234 | result += ", " ; |
| 235 | } |
| 236 | result += KeywordHelper::WriteOptionallyQuoted(text: unpivot_names[n]); |
| 237 | } |
| 238 | result += ")" ; |
| 239 | } |
| 240 | } |
| 241 | result += " FOR" ; |
| 242 | for (auto &pivot : pivots) { |
| 243 | result += " " ; |
| 244 | result += pivot.ToString(); |
| 245 | } |
| 246 | if (!groups.empty()) { |
| 247 | result += " GROUP BY " ; |
| 248 | for (idx_t i = 0; i < groups.size(); i++) { |
| 249 | if (i > 0) { |
| 250 | result += ", " ; |
| 251 | } |
| 252 | result += groups[i]; |
| 253 | } |
| 254 | } |
| 255 | result += ")" ; |
| 256 | if (!alias.empty()) { |
| 257 | result += " AS " + KeywordHelper::WriteOptionallyQuoted(text: alias); |
| 258 | if (!column_name_alias.empty()) { |
| 259 | result += "(" ; |
| 260 | for (idx_t i = 0; i < column_name_alias.size(); i++) { |
| 261 | if (i > 0) { |
| 262 | result += ", " ; |
| 263 | } |
| 264 | result += KeywordHelper::WriteOptionallyQuoted(text: column_name_alias[i]); |
| 265 | } |
| 266 | result += ")" ; |
| 267 | } |
| 268 | } |
| 269 | return result; |
| 270 | } |
| 271 | |
| 272 | bool PivotRef::Equals(const TableRef &other_p) const { |
| 273 | if (!TableRef::Equals(other: other_p)) { |
| 274 | return false; |
| 275 | } |
| 276 | auto &other = other_p.Cast<PivotRef>(); |
| 277 | if (!source->Equals(other: *other.source)) { |
| 278 | return false; |
| 279 | } |
| 280 | if (!ParsedExpression::ListEquals(left: aggregates, right: other.aggregates)) { |
| 281 | return false; |
| 282 | } |
| 283 | if (pivots.size() != other.pivots.size()) { |
| 284 | return false; |
| 285 | } |
| 286 | for (idx_t i = 0; i < pivots.size(); i++) { |
| 287 | if (!pivots[i].Equals(other: other.pivots[i])) { |
| 288 | return false; |
| 289 | } |
| 290 | } |
| 291 | if (unpivot_names != other.unpivot_names) { |
| 292 | return false; |
| 293 | } |
| 294 | if (alias != other.alias) { |
| 295 | return false; |
| 296 | } |
| 297 | if (groups != other.groups) { |
| 298 | return false; |
| 299 | } |
| 300 | if (include_nulls != other.include_nulls) { |
| 301 | return false; |
| 302 | } |
| 303 | return true; |
| 304 | } |
| 305 | |
| 306 | unique_ptr<TableRef> PivotRef::Copy() { |
| 307 | auto copy = make_uniq<PivotRef>(); |
| 308 | copy->source = source->Copy(); |
| 309 | for (auto &aggr : aggregates) { |
| 310 | copy->aggregates.push_back(x: aggr->Copy()); |
| 311 | } |
| 312 | copy->unpivot_names = unpivot_names; |
| 313 | for (auto &entry : pivots) { |
| 314 | copy->pivots.push_back(x: entry.Copy()); |
| 315 | } |
| 316 | copy->groups = groups; |
| 317 | copy->column_name_alias = column_name_alias; |
| 318 | copy->include_nulls = include_nulls; |
| 319 | copy->alias = alias; |
| 320 | return std::move(copy); |
| 321 | } |
| 322 | |
| 323 | void PivotRef::Serialize(FieldWriter &writer) const { |
| 324 | writer.WriteSerializable(element: *source); |
| 325 | writer.WriteSerializableList(elements: aggregates); |
| 326 | writer.WriteList<string>(elements: unpivot_names); |
| 327 | writer.WriteRegularSerializableList(elements: pivots); |
| 328 | writer.WriteList<string>(elements: groups); |
| 329 | writer.WriteList<string>(elements: column_name_alias); |
| 330 | writer.WriteField<bool>(element: include_nulls); |
| 331 | } |
| 332 | |
| 333 | void PivotRef::FormatSerialize(FormatSerializer &serializer) const { |
| 334 | TableRef::FormatSerialize(serializer); |
| 335 | serializer.WriteProperty(tag: "source" , value: source); |
| 336 | serializer.WriteProperty(tag: "aggregates" , value: aggregates); |
| 337 | serializer.WriteProperty(tag: "unpivot_names" , value: unpivot_names); |
| 338 | serializer.WriteProperty(tag: "pivots" , value: pivots); |
| 339 | serializer.WriteProperty(tag: "groups" , value: groups); |
| 340 | serializer.WriteProperty(tag: "column_name_alias" , value: column_name_alias); |
| 341 | serializer.WriteProperty(tag: "include_nulls" , value: include_nulls); |
| 342 | } |
| 343 | |
| 344 | unique_ptr<TableRef> PivotRef::Deserialize(FieldReader &reader) { |
| 345 | auto result = make_uniq<PivotRef>(); |
| 346 | result->source = reader.ReadRequiredSerializable<TableRef>(); |
| 347 | result->aggregates = reader.ReadRequiredSerializableList<ParsedExpression>(); |
| 348 | result->unpivot_names = reader.ReadRequiredList<string>(); |
| 349 | result->pivots = reader.ReadRequiredSerializableList<PivotColumn, PivotColumn>(); |
| 350 | result->groups = reader.ReadRequiredList<string>(); |
| 351 | result->column_name_alias = reader.ReadRequiredList<string>(); |
| 352 | result->include_nulls = reader.ReadRequired<bool>(); |
| 353 | return std::move(result); |
| 354 | } |
| 355 | |
| 356 | unique_ptr<TableRef> PivotRef::FormatDeserialize(FormatDeserializer &source) { |
| 357 | auto result = make_uniq<PivotRef>(); |
| 358 | source.ReadProperty(tag: "source" , ret&: result->source); |
| 359 | source.ReadProperty(tag: "aggregates" , ret&: result->aggregates); |
| 360 | source.ReadProperty(tag: "unpivot_names" , ret&: result->unpivot_names); |
| 361 | source.ReadProperty(tag: "pivots" , ret&: result->pivots); |
| 362 | source.ReadProperty(tag: "groups" , ret&: result->groups); |
| 363 | source.ReadProperty(tag: "column_name_alias" , ret&: result->column_name_alias); |
| 364 | source.ReadProperty(tag: "include_nulls" , ret&: result->include_nulls); |
| 365 | return std::move(result); |
| 366 | } |
| 367 | |
| 368 | } // namespace duckdb |
| 369 | |