1 /** 2 * @file attribute.h 3 * 4 * @author Ravi Gaddipati 5 * 6 * @section LICENSE 7 * 8 * The MIT License 9 * 10 * @copyright Copyright (c) 2017-2021 TileDB, Inc. 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a copy 13 * of this software and associated documentation files (the "Software"), to deal 14 * in the Software without restriction, including without limitation the rights 15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 * copies of the Software, and to permit persons to whom the Software is 17 * furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice shall be included in 20 * all copies or substantial portions of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 * THE SOFTWARE. 29 * 30 * @section DESCRIPTION 31 * 32 * This file declares the C++ API for the TileDB Attribute object. 33 */ 34 35 #ifndef TILEDB_CPP_API_ATTRIBUTE_H 36 #define TILEDB_CPP_API_ATTRIBUTE_H 37 38 #include "context.h" 39 #include "deleter.h" 40 #include "exception.h" 41 #include "filter_list.h" 42 #include "object.h" 43 #include "tiledb.h" 44 #include "type.h" 45 46 #include <array> 47 #include <functional> 48 #include <memory> 49 #include <type_traits> 50 51 namespace tiledb { 52 53 /** 54 * Describes an attribute of an Array cell. 55 * 56 * @details 57 * An attribute specifies a name and datatype for a particular value in each 58 * array cell. There are 3 supported attribute types: 59 * 60 * - Fundamental types, such as `char`, `int`, `double`, `uint64_t`, etc.. 61 * - Fixed sized arrays: `T[N]` or `std::array<T, N>`, where T is a fundamental 62 * type 63 * - Variable length data: `std::string`, `std::vector<T>` where T is a 64 * fundamental type 65 * 66 * Fixed-size array types using POD types like `std::array<T, N>` are internally 67 * converted to byte-array attributes. E.g. an attribute of type 68 * `std::array<float, 3>` will be created as an attribute of type `TILEDB_CHAR` 69 * with cell_val_num `sizeof(std::array<float, 3>)`. 70 * 71 * Therefore, for fixed-length attributes it is recommended to use C-style 72 * arrays instead, e.g. `float[3]` instead of `std::array<float, 3>`. 73 * 74 * **Example:** 75 * 76 * @code{.cpp} 77 * tiledb::Context ctx; 78 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 79 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 80 * auto a3 = tiledb::Attribute::create<float[3]>(ctx, "a3"); 81 * 82 * // Change compression scheme 83 * tiledb::FilterList filters(ctx); 84 * filters.add_filter({ctx, TILEDB_FILTER_BZIP2}); 85 * a1.set_filter_list(filters); 86 * 87 * // Add attributes to a schema 88 * tiledb::ArraySchema schema(ctx, TILEDB_DENSE); 89 * schema.add_attributes(a1, a2, a3); 90 * @endcode 91 */ 92 class Attribute { 93 public: 94 /* ********************************* */ 95 /* CONSTRUCTORS & DESTRUCTORS */ 96 /* ********************************* */ 97 Attribute(const Context & ctx,tiledb_attribute_t * attr)98 Attribute(const Context& ctx, tiledb_attribute_t* attr) 99 : ctx_(ctx) { 100 attr_ = std::shared_ptr<tiledb_attribute_t>(attr, deleter_); 101 } 102 103 /** 104 * Construct an attribute with a name and enumerated type. `cell_val_num` will 105 * be set to 1. 106 * 107 * @param ctx TileDB context 108 * @param name Name of attribute 109 * @param type Enumerated type of attribute 110 */ Attribute(const Context & ctx,const std::string & name,tiledb_datatype_t type)111 Attribute(const Context& ctx, const std::string& name, tiledb_datatype_t type) 112 : ctx_(ctx) { 113 init_from_type(name, type); 114 } 115 116 /** Construct an attribute with an enumerated type and given filter list. */ Attribute(const Context & ctx,const std::string & name,tiledb_datatype_t type,const FilterList & filter_list)117 Attribute( 118 const Context& ctx, 119 const std::string& name, 120 tiledb_datatype_t type, 121 const FilterList& filter_list) 122 : ctx_(ctx) { 123 init_from_type(name, type); 124 set_filter_list(filter_list); 125 } 126 127 Attribute(const Attribute&) = default; 128 Attribute(Attribute&&) = default; 129 Attribute& operator=(const Attribute&) = default; 130 Attribute& operator=(Attribute&&) = default; 131 132 /* ********************************* */ 133 /* API */ 134 /* ********************************* */ 135 136 /** Returns the name of the attribute. */ name()137 std::string name() const { 138 auto& ctx = ctx_.get(); 139 const char* name; 140 ctx.handle_error( 141 tiledb_attribute_get_name(ctx.ptr().get(), attr_.get(), &name)); 142 return name; 143 } 144 145 /** Returns the attribute datatype. */ type()146 tiledb_datatype_t type() const { 147 auto& ctx = ctx_.get(); 148 tiledb_datatype_t type; 149 ctx.handle_error( 150 tiledb_attribute_get_type(ctx.ptr().get(), attr_.get(), &type)); 151 return type; 152 } 153 154 /** 155 * Returns the size (in bytes) of one cell on this attribute. For 156 * variable-sized attributes returns TILEDB_VAR_NUM. 157 * 158 * **Example:** 159 * @code{.cpp} 160 * tiledb::Context ctx; 161 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 162 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 163 * auto a3 = tiledb::Attribute::create<float[3]>(ctx, "a3"); 164 * auto a4 = tiledb::Attribute::create<std::array<float, 3>>(ctx, "a4"); 165 * a1.cell_size(); // Returns sizeof(int) 166 * a2.cell_size(); // Variable sized attribute, returns TILEDB_VAR_NUM 167 * a3.cell_size(); // Returns 3 * sizeof(float) 168 * a4.cell_size(); // Stored as byte array, returns sizeof(char). 169 * @endcode 170 */ cell_size()171 uint64_t cell_size() const { 172 auto& ctx = ctx_.get(); 173 uint64_t cell_size; 174 ctx.handle_error(tiledb_attribute_get_cell_size( 175 ctx.ptr().get(), attr_.get(), &cell_size)); 176 return cell_size; 177 } 178 179 /** 180 * Returns number of values of one cell on this attribute. For variable-sized 181 * attributes returns TILEDB_VAR_NUM. 182 * 183 * **Example:** 184 * @code{.cpp} 185 * tiledb::Context ctx; 186 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 187 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 188 * auto a3 = tiledb::Attribute::create<float[3]>(ctx, "a3"); 189 * auto a4 = tiledb::Attribute::create<std::array<float, 3>>(ctx, "a4"); 190 * a1.cell_val_num(); // Returns 1 191 * a2.cell_val_num(); // Variable sized attribute, returns TILEDB_VAR_NUM 192 * a3.cell_val_num(); // Returns 3 193 * a4.cell_val_num(); // Stored as byte array, returns 194 * sizeof(std::array<float, 3>). 195 * @endcode 196 */ cell_val_num()197 unsigned cell_val_num() const { 198 auto& ctx = ctx_.get(); 199 unsigned num; 200 ctx.handle_error( 201 tiledb_attribute_get_cell_val_num(ctx.ptr().get(), attr_.get(), &num)); 202 return num; 203 } 204 205 /** 206 * Sets the number of attribute values per cell. This is inferred from 207 * the type parameter of the `Attribute::create<T>()` function, but can also 208 * be set manually. 209 * 210 * **Example:** 211 * @code{.cpp} 212 * // a1 and a2 are equivalent: 213 * auto a1 = Attribute::create<std::vector<int>>(...); 214 * auto a2 = Attribute::create<int>(...); 215 * a2.set_cell_val_num(TILEDB_VAR_NUM); 216 * @endcode 217 * 218 * @param num Cell val number to set. 219 * @return Reference to this Attribute 220 */ set_cell_val_num(unsigned num)221 Attribute& set_cell_val_num(unsigned num) { 222 auto& ctx = ctx_.get(); 223 ctx.handle_error( 224 tiledb_attribute_set_cell_val_num(ctx.ptr().get(), attr_.get(), num)); 225 return *this; 226 } 227 228 /** 229 * Sets the default fill value for the input attribute. This value will 230 * be used for the input attribute whenever querying (1) an empty cell in 231 * a dense array, or (2) a non-empty cell (in either dense or sparse array) 232 * when values on the input attribute are missing (e.g., if the user writes 233 * a subset of the attributes in a write operation). 234 * 235 * Applicable to var-sized attributes. 236 * 237 * **Example:** 238 * 239 * @code{.c} 240 * tiledb::Context ctx; 241 * 242 * // Fixed-sized attribute 243 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 244 * int32_t value = 0; 245 * uint64_t size = sizeof(value); 246 * a1.set_fill_value(&value, size); 247 * 248 * // Var-sized attribute 249 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 250 * std::string value("null"); 251 * a2.set_fill_value(value.c_str(), value.size()); 252 * @endcode 253 * 254 * @param value The fill value to set. 255 * @param size The fill value size in bytes. 256 * 257 * @note A call to `cell_val_num` sets the fill value 258 * of the attribute to its default. Therefore, make sure you invoke 259 * `set_fill_value` after deciding on the number 260 * of values this attribute will hold in each cell. 261 * 262 * @note For fixed-sized attributes, the input `size` should be equal 263 * to the cell size. 264 */ set_fill_value(const void * value,uint64_t size)265 Attribute& set_fill_value(const void* value, uint64_t size) { 266 auto& ctx = ctx_.get(); 267 ctx.handle_error(tiledb_attribute_set_fill_value( 268 ctx.ptr().get(), attr_.get(), value, size)); 269 return *this; 270 } 271 272 /** 273 * Gets the default fill value for the input attribute. This value will 274 * be used for the input attribute whenever querying (1) an empty cell in 275 * a dense array, or (2) a non-empty cell (in either dense or sparse array) 276 * when values on the input attribute are missing (e.g., if the user writes 277 * a subset of the attributes in a write operation). 278 * 279 * Applicable to both fixed-sized and var-sized attributes. 280 * 281 * **Example:** 282 * 283 * @code{.c} 284 * // Fixed-sized attribute 285 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 286 * const int32_t* value; 287 * uint64_t size; 288 * a1.get_fill_value(&value, &size); 289 * 290 * // Var-sized attribute 291 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 292 * const char* value; 293 * uint64_t size; 294 * a2.get_fill_value(&value, &size); 295 * @endcode 296 * 297 * @param value A pointer to the fill value to get. 298 * @param size The size of the fill value to get. 299 */ get_fill_value(const void ** value,uint64_t * size)300 void get_fill_value(const void** value, uint64_t* size) { 301 auto& ctx = ctx_.get(); 302 ctx.handle_error(tiledb_attribute_get_fill_value( 303 ctx.ptr().get(), attr_.get(), value, size)); 304 } 305 306 /** 307 * Sets the default fill value for the input, nullable attribute. This value 308 * will be used for the input attribute whenever querying (1) an empty cell in 309 * a dense array, or (2) a non-empty cell (in either dense or sparse array) 310 * when values on the input attribute are missing (e.g., if the user writes 311 * a subset of the attributes in a write operation). 312 * 313 * Applicable to var-sized attributes. 314 * 315 * **Example:** 316 * 317 * @code{.c} 318 * tiledb::Context ctx; 319 * 320 * // Fixed-sized attribute 321 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 322 * a1.set_nullable(true); 323 * int32_t value = 0; 324 * uint64_t size = sizeof(value); 325 * uint8_t valid = 0; 326 * a1.set_fill_value(&value, size, valid); 327 * 328 * // Var-sized attribute 329 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 330 * a2.set_nullable(true); 331 * std::string value("null"); 332 * uint8_t valid = 0; 333 * a2.set_fill_value(value.c_str(), value.size(), valid); 334 * @endcode 335 * 336 * @param value The fill value to set. 337 * @param size The fill value size in bytes. 338 * @param valid The validity fill value, zero for a null value and 339 * non-zero for a valid attribute. 340 * 341 * @note A call to `cell_val_num` sets the fill value 342 * of the attribute to its default. Therefore, make sure you invoke 343 * `set_fill_value` after deciding on the number 344 * of values this attribute will hold in each cell. 345 * 346 * @note For fixed-sized attributes, the input `size` should be equal 347 * to the cell size. 348 */ set_fill_value(const void * value,uint64_t size,uint8_t valid)349 Attribute& set_fill_value(const void* value, uint64_t size, uint8_t valid) { 350 auto& ctx = ctx_.get(); 351 ctx.handle_error(tiledb_attribute_set_fill_value_nullable( 352 ctx.ptr().get(), attr_.get(), value, size, valid)); 353 return *this; 354 } 355 356 /** 357 * Gets the default fill value for the input attribute. This value will 358 * be used for the input attribute whenever querying (1) an empty cell in 359 * a dense array, or (2) a non-empty cell (in either dense or sparse array) 360 * when values on the input attribute are missing (e.g., if the user writes 361 * a subset of the attributes in a write operation). 362 * 363 * Applicable to both fixed-sized and var-sized attributes. 364 * 365 * **Example:** 366 * 367 * @code{.c} 368 * // Fixed-sized attribute 369 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 370 * a1.set_nullable(true); 371 * const int32_t* value; 372 * uint64_t size; 373 * uint8_t valid; 374 * a1.get_fill_value(&value, &size, &valid); 375 * 376 * // Var-sized attribute 377 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 378 * a2.set_nullable(true); 379 * const char* value; 380 * uint64_t size; 381 * uint8_t valid; 382 * a2.get_fill_value(&value, &size, &valid); 383 * @endcode 384 * 385 * @param value A pointer to the fill value to get. 386 * @param size The size of the fill value to get. 387 * @param valid The fill value validity to get. 388 */ get_fill_value(const void ** value,uint64_t * size,uint8_t * valid)389 void get_fill_value(const void** value, uint64_t* size, uint8_t* valid) { 390 auto& ctx = ctx_.get(); 391 ctx.handle_error(tiledb_attribute_get_fill_value_nullable( 392 ctx.ptr().get(), attr_.get(), value, size, valid)); 393 } 394 395 /** Check if attribute is variable sized. **/ variable_sized()396 bool variable_sized() const { 397 return cell_val_num() == TILEDB_VAR_NUM; 398 } 399 400 /** 401 * Returns a copy of the FilterList of the attribute. 402 * To change the filter list, use `set_filter_list()`. 403 * 404 * @return Copy of the attribute FilterList. 405 */ filter_list()406 FilterList filter_list() const { 407 auto& ctx = ctx_.get(); 408 tiledb_filter_list_t* filter_list; 409 ctx.handle_error(tiledb_attribute_get_filter_list( 410 ctx.ptr().get(), attr_.get(), &filter_list)); 411 return FilterList(ctx, filter_list); 412 } 413 414 /** 415 * Sets the attribute filter list, which is an ordered list of filters that 416 * will be used to process and/or transform the attribute data (such as 417 * compression). 418 * 419 * @param filter_list Filter list to set 420 * @return Reference to this Attribute 421 */ set_filter_list(const FilterList & filter_list)422 Attribute& set_filter_list(const FilterList& filter_list) { 423 auto& ctx = ctx_.get(); 424 ctx.handle_error(tiledb_attribute_set_filter_list( 425 ctx.ptr().get(), attr_.get(), filter_list.ptr().get())); 426 return *this; 427 } 428 429 /** 430 * Sets the nullability of an attribute. 431 * 432 * **Example:** 433 * @code{.cpp} 434 * auto a1 = Attribute::create<int>(...); 435 * a1.set_nullable(true); 436 * @endcode 437 * 438 * @param nullable Whether the attribute is nullable. 439 * @return Reference to this Attribute 440 */ set_nullable(bool nullable)441 Attribute& set_nullable(bool nullable) { 442 auto& ctx = ctx_.get(); 443 ctx.handle_error(tiledb_attribute_set_nullable( 444 ctx.ptr().get(), attr_.get(), static_cast<uint8_t>(nullable))); 445 return *this; 446 } 447 448 /** 449 * Gets the nullability of an attribute. 450 * 451 * **Example:** 452 * @code{.cpp} 453 * auto a1 = Attribute::create<int>(...); 454 * auto nullable = a1.nullable(); 455 * @endcode 456 * 457 * @return Whether the attribute is nullable. 458 */ nullable()459 bool nullable() const { 460 auto& ctx = ctx_.get(); 461 uint8_t nullable; 462 ctx.handle_error( 463 tiledb_attribute_get_nullable(ctx.ptr().get(), attr_.get(), &nullable)); 464 return static_cast<bool>(nullable); 465 } 466 467 /** Returns the C TileDB attribute object pointer. */ ptr()468 std::shared_ptr<tiledb_attribute_t> ptr() const { 469 return attr_; 470 } 471 472 /** 473 * Dumps information about the attribute in an ASCII representation to an 474 * output. 475 * 476 * @param out (Optional) File to dump output to. Defaults to `nullptr` 477 * which will lead to selection of `stdout`. 478 */ 479 void dump(FILE* out = nullptr) const { 480 ctx_.get().handle_error( 481 tiledb_attribute_dump(ctx_.get().ptr().get(), attr_.get(), out)); 482 } 483 484 /* ********************************* */ 485 /* STATIC FUNCTIONS */ 486 /* ********************************* */ 487 488 /** 489 * Factory function for creating a new attribute with datatype T. 490 * 491 * **Example:** 492 * @code{.cpp} 493 * tiledb::Context ctx; 494 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1"); 495 * auto a2 = tiledb::Attribute::create<std::string>(ctx, "a2"); 496 * auto a3 = tiledb::Attribute::create<std::array<float, 3>>(ctx, "a3"); 497 * auto a4 = tiledb::Attribute::create<std::vector<double>>(ctx, "a4"); 498 * auto a5 = tiledb::Attribute::create<char[8]>(ctx, "a5"); 499 * @endcode 500 * 501 * @tparam T Datatype of the attribute. Can either be arithmetic type, 502 * C-style array, std::string, std::vector, or any trivially 503 * copyable classes (defined by std::is_trivially_copyable). 504 * @param ctx The TileDB context. 505 * @param name The attribute name. 506 * @return A new Attribute object. 507 */ 508 template <typename T> create(const Context & ctx,const std::string & name)509 static Attribute create(const Context& ctx, const std::string& name) { 510 using DataT = typename impl::TypeHandler<T>; 511 Attribute a(ctx, name, DataT::tiledb_type); 512 a.set_cell_val_num(DataT::tiledb_num); 513 return a; 514 } 515 516 /** Factory function taking the type as a tiledb_datatype_t variable. */ create(const Context & ctx,const std::string & name,tiledb_datatype_t type)517 static Attribute create( 518 const Context& ctx, const std::string& name, tiledb_datatype_t type) { 519 Attribute a(ctx, name, type); 520 return a; 521 } 522 523 /** 524 * Factory function for creating a new attribute with datatype T and 525 * a FilterList. 526 * 527 * **Example:** 528 * @code{.cpp} 529 * tiledb::Context ctx; 530 * tiledb::FilterList filter_list(ctx); 531 * filter_list.add_filter({ctx, TILEDB_FILTER_BYTESHUFFLE}) 532 * .add_filter({ctx, TILEDB_FILTER_BZIP2}); 533 * auto a1 = tiledb::Attribute::create<int>(ctx, "a1", filter_list); 534 * @endcode 535 * 536 * @tparam T Datatype of the attribute. Can either be arithmetic type, 537 * C-style array, `std::string`, `std::vector`, or any trivially 538 * copyable classes (defined by `std::is_trivially_copyable`). 539 * @param ctx The TileDB context. 540 * @param name The attribute name. 541 * @param filter_list FilterList to use for attribute 542 * @return A new Attribute object. 543 */ 544 template <typename T> create(const Context & ctx,const std::string & name,const FilterList & filter_list)545 static Attribute create( 546 const Context& ctx, 547 const std::string& name, 548 const FilterList& filter_list) { 549 auto a = create<T>(ctx, name); 550 a.set_filter_list(filter_list); 551 return a; 552 } 553 554 private: 555 /* ********************************* */ 556 /* PRIVATE ATTRIBUTES */ 557 /* ********************************* */ 558 559 /** The TileDB context. */ 560 std::reference_wrapper<const Context> ctx_; 561 562 /** An auxiliary deleter. */ 563 impl::Deleter deleter_; 564 565 /** The pointer to the C TileDB attribute object. */ 566 std::shared_ptr<tiledb_attribute_t> attr_; 567 568 /* ********************************* */ 569 /* PRIVATE FUNCTIONS */ 570 /* ********************************* */ 571 init_from_type(const std::string & name,tiledb_datatype_t type)572 void init_from_type(const std::string& name, tiledb_datatype_t type) { 573 tiledb_attribute_t* attr; 574 auto& ctx = ctx_.get(); 575 ctx.handle_error( 576 tiledb_attribute_alloc(ctx.ptr().get(), name.c_str(), type, &attr)); 577 attr_ = std::shared_ptr<tiledb_attribute_t>(attr, deleter_); 578 } 579 }; 580 581 /* ********************************* */ 582 /* MISC */ 583 /* ********************************* */ 584 585 /** Gets a string representation of an attribute for an output stream. */ 586 inline std::ostream& operator<<(std::ostream& os, const Attribute& a) { 587 os << "Attr<" << a.name() << ',' << tiledb::impl::to_str(a.type()) << ',' 588 << (a.cell_val_num() == TILEDB_VAR_NUM ? "VAR" : 589 std::to_string(a.cell_val_num())) 590 << '>'; 591 return os; 592 } 593 594 } // namespace tiledb 595 596 #endif // TILEDB_CPP_API_ATTRIBUTE_H 597