| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| **/ |
| |
| #include "types/VarCharType.hpp" |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <string> |
| #include <utility> |
| |
| #include "types/NullCoercibilityCheckMacro.hpp" |
| #include "types/Type.hpp" |
| #include "types/Type.pb.h" |
| #include "types/TypeID.hpp" |
| #include "types/TypedValue.hpp" |
| #include "types/port/strnlen.hpp" |
| #include "utility/PtrMap.hpp" |
| |
| #include "glog/logging.h" |
| |
| using std::pair; |
| using std::size_t; |
| using std::strcmp; |
| using std::string; |
| |
| namespace quickstep { |
| |
| template <bool nullable_internal> |
| const VarCharType& VarCharType::InstanceInternal(const std::size_t length) { |
| static PtrMap<size_t, VarCharType> instance_map; |
| PtrMap<size_t, VarCharType>::iterator imit = instance_map.find(length); |
| if (imit == instance_map.end()) { |
| imit = instance_map.insert(length, new VarCharType(length, nullable_internal)).first; |
| } |
| return *(imit->second); |
| } |
| |
| const VarCharType& VarCharType::InstanceNonNullable(const std::size_t length) { |
| return InstanceInternal<false>(length); |
| } |
| |
| const VarCharType& VarCharType::InstanceNullable(const std::size_t length) { |
| return InstanceInternal<true>(length); |
| } |
| |
| const VarCharType& VarCharType::InstanceFromProto(const serialization::Type &proto) { |
| return Instance(proto.GetExtension(serialization::VarCharType::length), proto.nullable()); |
| } |
| |
| serialization::Type VarCharType::getProto() const { |
| serialization::Type proto; |
| proto.set_type_id(serialization::Type::VAR_CHAR); |
| |
| proto.set_nullable(nullable_); |
| |
| proto.SetExtension(serialization::VarCharType::length, length_); |
| return proto; |
| } |
| |
| size_t VarCharType::estimateAverageByteLength() const { |
| if (length_ > 160) { |
| return 80; |
| } else { |
| return (length_ >> 1) + 1; |
| } |
| } |
| |
| bool VarCharType::isSafelyCoercibleFrom(const Type &original_type) const { |
| QUICKSTEP_NULL_COERCIBILITY_CHECK(); |
| switch (original_type.getTypeID()) { |
| case kChar: |
| return original_type.maximumByteLength() <= length_; |
| case kVarChar: |
| return original_type.maximumByteLength() <= length_ + 1; |
| default: |
| return false; |
| } |
| } |
| |
| bool VarCharType::isSubsumedBy(const Type &original_type) const { |
| if (Type::isSubsumedBy(original_type)) { |
| return true; |
| } |
| |
| if (original_type.getTypeID() == kVarChar) { |
| if (maximum_byte_length_ <= original_type.maximumByteLength()) { |
| return (!nullable_ || original_type.isNullable()); |
| } |
| } |
| |
| return false; |
| } |
| |
| string VarCharType::getName() const { |
| string name("VarChar("); |
| name.append(std::to_string(length_)); |
| name.push_back(')'); |
| if (nullable_) { |
| name.append(" NULL"); |
| } |
| return name; |
| } |
| |
| std::string VarCharType::printValueToString(const TypedValue &value) const { |
| DCHECK(!value.isNull()); |
| |
| return std::string(static_cast<const char*>(value.getOutOfLineData())); |
| } |
| |
| void VarCharType::printValueToFile(const TypedValue &value, |
| FILE *file, |
| const int padding) const { |
| DCHECK(!value.isNull()); |
| |
| std::fprintf(file, |
| "%*s", |
| static_cast<int>(padding), |
| static_cast<const char*>(value.getOutOfLineData())); |
| } |
| |
| bool VarCharType::parseValueFromString(const std::string &value_string, |
| TypedValue *value) const { |
| if (value_string.length() > length_) { |
| return false; |
| } |
| |
| *value = TypedValue(kVarChar, value_string.c_str(), value_string.length() + 1); |
| value->ensureNotReference(); |
| return true; |
| } |
| |
| TypedValue VarCharType::coerceValue(const TypedValue &original_value, |
| const Type &original_type) const { |
| DCHECK(isCoercibleFrom(original_type)) |
| << "Can't coerce value of Type " << original_type.getName() |
| << " to Type " << getName(); |
| |
| if (original_value.isNull()) { |
| return makeNullValue(); |
| } |
| |
| const void *original_data = original_value.getOutOfLineData(); |
| const std::size_t original_data_size = original_value.getDataSize(); |
| const std::size_t original_strlen |
| = (original_type.getTypeID() == kVarChar) |
| ? original_data_size - 1 |
| : strnlen(static_cast<const char*>(original_data), original_data_size); |
| const bool null_terminated = original_strlen < original_data_size; |
| |
| if ((original_strlen <= length_) && null_terminated) { |
| TypedValue value_copy(original_value); |
| value_copy.markType(kVarChar); |
| return value_copy; |
| } |
| |
| const std::size_t copy_strlen = original_strlen < length_ |
| ? original_strlen |
| : length_; |
| char *copied_str = static_cast<char*>(std::malloc(copy_strlen + 1)); |
| std::memcpy(copied_str, original_data, copy_strlen); |
| copied_str[copy_strlen] = '\0'; |
| return TypedValue::CreateWithOwnedData(kVarChar, |
| copied_str, |
| copy_strlen + 1); |
| } |
| |
| } // namespace quickstep |