/** * @file attribute.cc * * @section LICENSE * * The MIT License * * @copyright Copyright (c) 2017-2020 TileDB, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * @section DESCRIPTION * * This file implements class Attribute. */ #include "tiledb/sm/array_schema/attribute.h" #include "tiledb/common/logger.h" #include "tiledb/sm/buffer/const_buffer.h" #include "tiledb/sm/enums/compressor.h" #include "tiledb/sm/enums/datatype.h" #include "tiledb/sm/enums/filter_type.h" #include "tiledb/sm/filter/compression_filter.h" #include "tiledb/sm/misc/utils.h" #include #include #include using namespace tiledb::common; namespace tiledb { namespace sm { /* ********************************* */ /* CONSTRUCTORS & DESTRUCTORS */ /* ********************************* */ Attribute::Attribute() : Attribute("", Datatype::CHAR, false) { } Attribute::Attribute( const std::string& name, const Datatype type, const bool nullable) { name_ = name; type_ = type; nullable_ = nullable; cell_val_num_ = (type == Datatype::ANY) ? constants::var_num : 1; set_default_fill_value(); } Attribute::Attribute(const Attribute* attr) { assert(attr != nullptr); name_ = attr->name(); type_ = attr->type(); cell_val_num_ = attr->cell_val_num(); nullable_ = attr->nullable(); filters_ = attr->filters_; fill_value_ = attr->fill_value_; fill_value_validity_ = attr->fill_value_validity_; } Attribute::~Attribute() = default; /* ********************************* */ /* API */ /* ********************************* */ uint64_t Attribute::cell_size() const { if (var_size()) return constants::var_size; return cell_val_num_ * datatype_size(type_); } unsigned int Attribute::cell_val_num() const { return cell_val_num_; } Status Attribute::deserialize(ConstBuffer* buff, const uint32_t version) { // Load attribute name uint32_t attribute_name_size; RETURN_NOT_OK(buff->read(&attribute_name_size, sizeof(uint32_t))); name_.resize(attribute_name_size); RETURN_NOT_OK(buff->read(&name_[0], attribute_name_size)); // Load type uint8_t type; RETURN_NOT_OK(buff->read(&type, sizeof(uint8_t))); type_ = (Datatype)type; // Load cell_val_num_ RETURN_NOT_OK(buff->read(&cell_val_num_, sizeof(uint32_t))); // Load filter pipeline RETURN_NOT_OK(filters_.deserialize(buff)); // Load fill value if (version >= 6) { uint64_t fill_value_size = 0; RETURN_NOT_OK(buff->read(&fill_value_size, sizeof(uint64_t))); assert(fill_value_size > 0); fill_value_.resize(fill_value_size); fill_value_.shrink_to_fit(); RETURN_NOT_OK(buff->read(&fill_value_[0], fill_value_size)); } else { set_default_fill_value(); } // Load nullable flag if (version >= 7) RETURN_NOT_OK(buff->read(&nullable_, sizeof(bool))); // Load validity fill value if (version >= 7) RETURN_NOT_OK(buff->read(&fill_value_validity_, sizeof(uint8_t))); return Status::Ok(); } void Attribute::dump(FILE* out) const { if (out == nullptr) out = stdout; // Dump fprintf(out, "### Attribute ###\n"); fprintf(out, "- Name: %s\n", name_.c_str()); fprintf(out, "- Type: %s\n", datatype_str(type_).c_str()); fprintf(out, "- Nullable: %s\n", (nullable_ ? "true" : "false")); if (!var_size()) fprintf(out, "- Cell val num: %u\n", cell_val_num_); else fprintf(out, "- Cell val num: var\n"); fprintf(out, "- Filters: %u", (unsigned)filters_.size()); filters_.dump(out); fprintf(out, "\n"); fprintf(out, "- Fill value: %s", fill_value_str().c_str()); if (nullable_) { fprintf(out, "\n"); fprintf(out, "- Fill value validity: %u", fill_value_validity_); } fprintf(out, "\n"); } const FilterPipeline& Attribute::filters() const { return filters_; } const std::string& Attribute::name() const { return name_; } // ===== FORMAT ===== // attribute_name_size (uint32_t) // attribute_name (string) // type (uint8_t) // cell_val_num (uint32_t) // filter_pipeline (see FilterPipeline::serialize) // fill_value_size (uint64_t) // fill_value (uint8_t[]) // nullable (bool) // fill_value_validity (uint8_t) Status Attribute::serialize(Buffer* buff, const uint32_t version) { // Write attribute name auto attribute_name_size = (uint32_t)name_.size(); RETURN_NOT_OK(buff->write(&attribute_name_size, sizeof(uint32_t))); RETURN_NOT_OK(buff->write(name_.c_str(), attribute_name_size)); // Write type auto type = (uint8_t)type_; RETURN_NOT_OK(buff->write(&type, sizeof(uint8_t))); // Write cell_val_num_ RETURN_NOT_OK(buff->write(&cell_val_num_, sizeof(uint32_t))); // Write filter pipeline RETURN_NOT_OK(filters_.serialize(buff)); // Write fill value if (version >= 6) { auto fill_value_size = (uint64_t)fill_value_.size(); assert(fill_value_size != 0); RETURN_NOT_OK(buff->write(&fill_value_size, sizeof(uint64_t))); RETURN_NOT_OK(buff->write(&fill_value_[0], fill_value_.size())); } // Write nullable if (version >= 7) RETURN_NOT_OK(buff->write(&nullable_, sizeof(bool))); // Write validity fill value if (version >= 7) RETURN_NOT_OK(buff->write(&fill_value_validity_, sizeof(uint8_t))); return Status::Ok(); } Status Attribute::set_cell_val_num(unsigned int cell_val_num) { if (type_ == Datatype::ANY) return LOG_STATUS(Status::AttributeError( "Cannot set number of values per cell; Attribute datatype `ANY` is " "always variable-sized")); cell_val_num_ = cell_val_num; set_default_fill_value(); return Status::Ok(); } Status Attribute::set_nullable(const bool nullable) { nullable_ = nullable; return Status::Ok(); } Status Attribute::get_nullable(bool* const nullable) { *nullable = nullable_; return Status::Ok(); } Status Attribute::set_filter_pipeline(const FilterPipeline* pipeline) { if (pipeline == nullptr) return LOG_STATUS(Status::AttributeError( "Cannot set filter pipeline to attribute; Pipeline cannot be null")); for (unsigned i = 0; i < pipeline->size(); ++i) { if (datatype_is_real(type_) && pipeline->get_filter(i)->type() == FilterType::FILTER_DOUBLE_DELTA) return LOG_STATUS( Status::AttributeError("Cannot set DOUBLE DELTA filter to a " "dimension with a real datatype")); } filters_ = *pipeline; return Status::Ok(); } void Attribute::set_name(const std::string& name) { name_ = name; } Status Attribute::set_fill_value(const void* value, uint64_t size) { if (value == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input value cannot be null")); } if (size == 0) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input size cannot be 0")); } if (nullable()) { return LOG_STATUS( Status::AttributeError("Cannot set fill value; Attribute is nullable")); } if (!var_size() && size != cell_size()) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input size is not the same as cell size")); } fill_value_.resize(size); fill_value_.shrink_to_fit(); std::memcpy(&fill_value_[0], value, size); return Status::Ok(); } Status Attribute::get_fill_value(const void** value, uint64_t* size) const { if (value == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot get fill value; Input value cannot be null")); } if (size == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot get fill value; Input size cannot be null")); } if (nullable()) { return LOG_STATUS( Status::AttributeError("Cannot get fill value; Attribute is nullable")); } *value = fill_value_.data(); *size = (uint64_t)fill_value_.size(); return Status::Ok(); } Status Attribute::set_fill_value( const void* value, uint64_t size, uint8_t valid) { if (value == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input value cannot be null")); } if (size == 0) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input size cannot be 0")); } if (!nullable()) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Attribute is not nullable")); } if (!var_size() && size != cell_size()) { return LOG_STATUS(Status::AttributeError( "Cannot set fill value; Input size is not the same as cell size")); } fill_value_.resize(size); fill_value_.shrink_to_fit(); std::memcpy(&fill_value_[0], value, size); fill_value_validity_ = valid; return Status::Ok(); } Status Attribute::get_fill_value( const void** value, uint64_t* size, uint8_t* valid) const { if (value == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot get fill value; Input value cannot be null")); } if (size == nullptr) { return LOG_STATUS(Status::AttributeError( "Cannot get fill value; Input size cannot be null")); } if (!nullable()) { return LOG_STATUS(Status::AttributeError( "Cannot get fill value; Attribute is not nullable")); } *value = fill_value_.data(); *size = (uint64_t)fill_value_.size(); *valid = fill_value_validity_; return Status::Ok(); } const ByteVecValue& Attribute::fill_value() const { return fill_value_; } uint8_t Attribute::fill_value_validity() const { return fill_value_validity_; } Datatype Attribute::type() const { return type_; } bool Attribute::var_size() const { return cell_val_num_ == constants::var_num; } bool Attribute::nullable() const { return nullable_; } /* ********************************* */ /* PRIVATE METHODS */ /* ********************************* */ void Attribute::set_default_fill_value() { auto fill_value = constants::fill_value(type_); auto fill_size = datatype_size(type_); auto cell_num = (var_size()) ? 1 : cell_val_num_; fill_value_.resize(cell_num * fill_size); fill_value_.shrink_to_fit(); uint64_t offset = 0; auto buff = (unsigned char*)&fill_value_[0]; for (uint64_t i = 0; i < cell_num; ++i) { std::memcpy(buff + offset, fill_value, fill_size); offset += fill_size; } fill_value_validity_ = 0; } std::string Attribute::fill_value_str() const { std::string ret; auto v_size = datatype_size(type_); uint64_t num = fill_value_.size() / v_size; auto v = fill_value_.data(); for (uint64_t i = 0; i < num; ++i) { ret += utils::parse::to_str(v, type_); v += v_size; if (i != num - 1) ret += ", "; } return ret; } } // namespace sm } // namespace tiledb