1 /**
2  * @file   array_schema_evolution.cc
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2018-2021 TileDB, Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * This file defines serialization functions for ArraySchemaEvolution.
31  */
32 #ifdef TILEDB_SERIALIZATION
33 #include <capnp/compat/json.h>
34 #include <capnp/serialize.h>
35 #include "tiledb/sm/serialization/capnp_utils.h"
36 #endif
37 
38 #include "tiledb/common/heap_memory.h"
39 #include "tiledb/common/logger.h"
40 #include "tiledb/sm/array/array.h"
41 #include "tiledb/sm/array_schema/array_schema_evolution.h"
42 #include "tiledb/sm/array_schema/attribute.h"
43 #include "tiledb/sm/array_schema/dimension.h"
44 #include "tiledb/sm/array_schema/domain.h"
45 #include "tiledb/sm/enums/array_type.h"
46 #include "tiledb/sm/enums/compressor.h"
47 #include "tiledb/sm/enums/datatype.h"
48 #include "tiledb/sm/enums/filter_option.h"
49 #include "tiledb/sm/enums/filter_type.h"
50 #include "tiledb/sm/enums/layout.h"
51 #include "tiledb/sm/enums/serialization_type.h"
52 #include "tiledb/sm/misc/constants.h"
53 #include "tiledb/sm/serialization/array_schema.h"
54 
55 #include <set>
56 
57 using namespace tiledb::common;
58 
59 namespace tiledb {
60 namespace sm {
61 namespace serialization {
62 
63 #ifdef TILEDB_SERIALIZATION
64 
array_schema_evolution_to_capnp(const ArraySchemaEvolution * array_schema_evolution,capnp::ArraySchemaEvolution::Builder * array_schema_evolution_builder,const bool client_side)65 Status array_schema_evolution_to_capnp(
66     const ArraySchemaEvolution* array_schema_evolution,
67     capnp::ArraySchemaEvolution::Builder* array_schema_evolution_builder,
68     const bool client_side) {
69   if (array_schema_evolution == nullptr)
70     return LOG_STATUS(
71         Status::SerializationError("Error serializing array schema evolution; "
72                                    "array schema evolution is null."));
73 
74   // Attributes to drop
75   std::vector<std::string> attr_names_to_drop =
76       array_schema_evolution->attribute_names_to_drop();
77 
78   auto attributes_to_drop_builder =
79       array_schema_evolution_builder->initAttributesToDrop(
80           attr_names_to_drop.size());
81 
82   for (size_t i = 0; i < attr_names_to_drop.size(); i++) {
83     attributes_to_drop_builder.set(i, attr_names_to_drop[i]);
84   }
85 
86   // Attributes to add
87   std::vector<std::string> attr_names_to_add =
88       array_schema_evolution->attribute_names_to_add();
89 
90   auto attributes_to_add_buidler =
91       array_schema_evolution_builder->initAttributesToAdd(
92           attr_names_to_add.size());
93 
94   for (size_t i = 0; i < attr_names_to_add.size(); i++) {
95     auto attribute_builder = attributes_to_add_buidler[i];
96     auto attr_name = attr_names_to_add[i];
97     const Attribute* attr_to_add =
98         array_schema_evolution->attribute_to_add(attr_name);
99     if (attr_to_add == nullptr) {
100       continue;
101     }
102     RETURN_NOT_OK(attribute_to_capnp(attr_to_add, &attribute_builder));
103   }
104 
105   return Status::Ok();
106 }
107 
array_schema_evolution_from_capnp(const capnp::ArraySchemaEvolution::Reader & evolution_reader,tdb_unique_ptr<ArraySchemaEvolution> * array_schema_evolution)108 Status array_schema_evolution_from_capnp(
109     const capnp::ArraySchemaEvolution::Reader& evolution_reader,
110     tdb_unique_ptr<ArraySchemaEvolution>* array_schema_evolution) {
111   array_schema_evolution->reset(tdb_new(ArraySchemaEvolution));
112   // Set attributes to drop
113   auto attributes_to_drop_reader = evolution_reader.getAttributesToDrop();
114   for (auto attr_reader : attributes_to_drop_reader) {
115     std::string attr_name = std::string(attr_reader.cStr());
116     RETURN_NOT_OK((*array_schema_evolution)->drop_attribute(attr_name));
117   }
118 
119   // Set attributes to add
120   auto attributes_to_add_reader = evolution_reader.getAttributesToAdd();
121   for (auto attr_reader : attributes_to_add_reader) {
122     tdb_unique_ptr<Attribute> attribute;
123     RETURN_NOT_OK(attribute_from_capnp(attr_reader, &attribute));
124     const Attribute* attr_to_add = attribute.get();
125     RETURN_NOT_OK((*array_schema_evolution)->add_attribute(attr_to_add));
126   }
127 
128   return Status::Ok();
129 }
130 
array_schema_evolution_serialize(ArraySchemaEvolution * array_schema_evolution,SerializationType serialize_type,Buffer * serialized_buffer,const bool client_side)131 Status array_schema_evolution_serialize(
132     ArraySchemaEvolution* array_schema_evolution,
133     SerializationType serialize_type,
134     Buffer* serialized_buffer,
135     const bool client_side) {
136   try {
137     ::capnp::MallocMessageBuilder message;
138     capnp::ArraySchemaEvolution::Builder arraySchemaEvolutionBuilder =
139         message.initRoot<capnp::ArraySchemaEvolution>();
140     RETURN_NOT_OK(array_schema_evolution_to_capnp(
141         array_schema_evolution, &arraySchemaEvolutionBuilder, client_side));
142 
143     serialized_buffer->reset_size();
144     serialized_buffer->reset_offset();
145 
146     switch (serialize_type) {
147       case SerializationType::JSON: {
148         ::capnp::JsonCodec json;
149         kj::String capnp_json = json.encode(arraySchemaEvolutionBuilder);
150         const auto json_len = capnp_json.size();
151         const char nul = '\0';
152         // size does not include needed null terminator, so add +1
153         RETURN_NOT_OK(serialized_buffer->realloc(json_len + 1));
154         RETURN_NOT_OK(serialized_buffer->write(capnp_json.cStr(), json_len));
155         RETURN_NOT_OK(serialized_buffer->write(&nul, 1));
156         break;
157       }
158       case SerializationType::CAPNP: {
159         kj::Array<::capnp::word> protomessage = messageToFlatArray(message);
160         kj::ArrayPtr<const char> message_chars = protomessage.asChars();
161         const auto nbytes = message_chars.size();
162         RETURN_NOT_OK(serialized_buffer->realloc(nbytes));
163         RETURN_NOT_OK(serialized_buffer->write(message_chars.begin(), nbytes));
164         break;
165       }
166       default: {
167         return LOG_STATUS(
168             Status::SerializationError("Error serializing array schema "
169                                        "evolution; Unknown serialization type "
170                                        "passed"));
171       }
172     }
173 
174   } catch (kj::Exception& e) {
175     return LOG_STATUS(Status::SerializationError(
176         "Error serializing array schema evolution; kj::Exception: " +
177         std::string(e.getDescription().cStr())));
178   } catch (std::exception& e) {
179     return LOG_STATUS(Status::SerializationError(
180         "Error serializing array schema evolution; exception " +
181         std::string(e.what())));
182   }
183 
184   return Status::Ok();
185 }
186 
array_schema_evolution_deserialize(ArraySchemaEvolution ** array_schema_evolution,SerializationType serialize_type,const Buffer & serialized_buffer)187 Status array_schema_evolution_deserialize(
188     ArraySchemaEvolution** array_schema_evolution,
189     SerializationType serialize_type,
190     const Buffer& serialized_buffer) {
191   try {
192     tdb_unique_ptr<ArraySchemaEvolution> decoded_array_schema_evolution =
193         nullptr;
194 
195     switch (serialize_type) {
196       case SerializationType::JSON: {
197         ::capnp::JsonCodec json;
198         ::capnp::MallocMessageBuilder message_builder;
199         capnp::ArraySchemaEvolution::Builder array_schema_evolution_builder =
200             message_builder.initRoot<capnp::ArraySchemaEvolution>();
201         json.decode(
202             kj::StringPtr(static_cast<const char*>(serialized_buffer.data())),
203             array_schema_evolution_builder);
204         capnp::ArraySchemaEvolution::Reader array_schema_evolution_reader =
205             array_schema_evolution_builder.asReader();
206         RETURN_NOT_OK(array_schema_evolution_from_capnp(
207             array_schema_evolution_reader, &decoded_array_schema_evolution));
208         break;
209       }
210       case SerializationType::CAPNP: {
211         const auto mBytes =
212             reinterpret_cast<const kj::byte*>(serialized_buffer.data());
213         ::capnp::FlatArrayMessageReader reader(kj::arrayPtr(
214             reinterpret_cast<const ::capnp::word*>(mBytes),
215             serialized_buffer.size() / sizeof(::capnp::word)));
216         capnp::ArraySchemaEvolution::Reader array_schema_evolution_reader =
217             reader.getRoot<capnp::ArraySchemaEvolution>();
218         RETURN_NOT_OK(array_schema_evolution_from_capnp(
219             array_schema_evolution_reader, &decoded_array_schema_evolution));
220         break;
221       }
222       default: {
223         return LOG_STATUS(
224             Status::SerializationError("Error deserializing array schema "
225                                        "evolution; Unknown serialization type "
226                                        "passed"));
227       }
228     }
229 
230     if (decoded_array_schema_evolution == nullptr)
231       return LOG_STATUS(Status::SerializationError(
232           "Error serializing array schema evolution; deserialized schema "
233           "evolution is null"));
234 
235     *array_schema_evolution = decoded_array_schema_evolution.release();
236   } catch (kj::Exception& e) {
237     return LOG_STATUS(Status::SerializationError(
238         "Error deserializing array schema evolution; kj::Exception: " +
239         std::string(e.getDescription().cStr())));
240   } catch (std::exception& e) {
241     return LOG_STATUS(Status::SerializationError(
242         "Error deserializing array schema evolution; exception " +
243         std::string(e.what())));
244   }
245 
246   return Status::Ok();
247 }
248 
249 #else
250 
251 Status array_schema_evolution_serialize(
252     ArraySchemaEvolution*, SerializationType, Buffer*, const bool) {
253   return LOG_STATUS(Status::SerializationError(
254       "Cannot serialize; serialization not enabled."));
255 }
256 
257 Status array_schema_evolution_deserialize(
258     ArraySchemaEvolution**, SerializationType, const Buffer&) {
259   return LOG_STATUS(Status::SerializationError(
260       "Cannot serialize; serialization not enabled."));
261 }
262 
263 #endif  // TILEDB_SERIALIZATION
264 
265 }  // namespace serialization
266 }  // namespace sm
267 }  // namespace tiledb