| // 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 "kudu/client/scan_batch.h" |
| |
| #include <cstring> |
| #include <string> |
| |
| #include <glog/logging.h> |
| |
| #include "kudu/client/row_result.h" |
| #include "kudu/client/scanner-internal.h" |
| #include "kudu/common/common.pb.h" |
| #include "kudu/common/schema.h" |
| #include "kudu/common/types.h" |
| #include "kudu/gutil/strings/stringpiece.h" |
| #include "kudu/gutil/strings/substitute.h" |
| #include "kudu/util/bitmap.h" |
| #include "kudu/util/int128.h" |
| #include "kudu/util/logging.h" |
| |
| using std::string; |
| using strings::Substitute; |
| |
| namespace kudu { |
| namespace client { |
| |
| //////////////////////////////////////////////////////////// |
| // KuduScanBatch |
| //////////////////////////////////////////////////////////// |
| |
| KuduScanBatch::KuduScanBatch() : data_(new Data()) {} |
| |
| KuduScanBatch::~KuduScanBatch() { |
| delete data_; |
| } |
| |
| int KuduScanBatch::NumRows() const { |
| return data_->num_rows(); |
| } |
| |
| KuduRowResult KuduScanBatch::Row(int idx) const { |
| return data_->row(idx); |
| } |
| |
| const KuduSchema* KuduScanBatch::projection_schema() const { |
| return data_->client_projection_; |
| } |
| |
| Slice KuduScanBatch::direct_data() const { |
| return data_->direct_data_; |
| } |
| |
| Slice KuduScanBatch::indirect_data() const { |
| return data_->indirect_data_; |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // KuduScanBatch::RowPtr |
| //////////////////////////////////////////////////////////// |
| |
| namespace { |
| |
| inline Status FindColumn(const Schema& schema, const Slice& col_name, int* idx) { |
| StringPiece sp(reinterpret_cast<const char*>(col_name.data()), col_name.size()); |
| *idx = schema.find_column(sp); |
| if (PREDICT_FALSE(*idx == -1)) { |
| return Status::NotFound("No such column", col_name); |
| } |
| return Status::OK(); |
| } |
| |
| // Just enough of a "cell" to support the Schema::DebugCellAppend calls |
| // made by KuduScanBatch::RowPtr::ToString. |
| class RowCell { |
| public: |
| RowCell(const KuduScanBatch::RowPtr* row, int idx) |
| : row_(row), |
| col_idx_(idx) { |
| } |
| |
| bool is_null() const { |
| return row_->IsNull(col_idx_); |
| } |
| const void* ptr() const { |
| return row_->cell(col_idx_); |
| } |
| |
| private: |
| const KuduScanBatch::RowPtr* row_; |
| const int col_idx_; |
| }; |
| |
| } // anonymous namespace |
| |
| bool KuduScanBatch::RowPtr::IsNull(int col_idx) const { |
| const ColumnSchema& col = schema_->column(col_idx); |
| if (!col.is_nullable()) { |
| return false; |
| } |
| |
| return BitmapTest(row_data_ + schema_->byte_size(), col_idx); |
| } |
| |
| bool KuduScanBatch::RowPtr::IsNull(const Slice& col_name) const { |
| int col_idx; |
| CHECK_OK(FindColumn(*schema_, col_name, &col_idx)); |
| return IsNull(col_idx); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetBool(const Slice& col_name, bool* val) const { |
| return Get<TypeTraits<BOOL> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt8(const Slice& col_name, int8_t* val) const { |
| return Get<TypeTraits<INT8> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt16(const Slice& col_name, int16_t* val) const { |
| return Get<TypeTraits<INT16> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt32(const Slice& col_name, int32_t* val) const { |
| return Get<TypeTraits<INT32> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt64(const Slice& col_name, int64_t* val) const { |
| return Get<TypeTraits<INT64> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetUnixTimeMicros(const Slice& col_name, int64_t* val) const { |
| return Get<TypeTraits<UNIXTIME_MICROS> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetFloat(const Slice& col_name, float* val) const { |
| return Get<TypeTraits<FLOAT> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetDouble(const Slice& col_name, double* val) const { |
| return Get<TypeTraits<DOUBLE> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetUnscaledDecimal(const Slice& col_name, int128_t* val) const { |
| int col_idx; |
| RETURN_NOT_OK(FindColumn(*schema_, col_name, &col_idx)); |
| return GetUnscaledDecimal(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetString(const Slice& col_name, Slice* val) const { |
| return Get<TypeTraits<STRING> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetBinary(const Slice& col_name, Slice* val) const { |
| return Get<TypeTraits<BINARY> >(col_name, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetBool(int col_idx, bool* val) const { |
| return Get<TypeTraits<BOOL> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt8(int col_idx, int8_t* val) const { |
| return Get<TypeTraits<INT8> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt16(int col_idx, int16_t* val) const { |
| return Get<TypeTraits<INT16> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt32(int col_idx, int32_t* val) const { |
| return Get<TypeTraits<INT32> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetInt64(int col_idx, int64_t* val) const { |
| return Get<TypeTraits<INT64> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetUnixTimeMicros(int col_idx, int64_t* val) const { |
| return Get<TypeTraits<UNIXTIME_MICROS> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetFloat(int col_idx, float* val) const { |
| return Get<TypeTraits<FLOAT> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetDouble(int col_idx, double* val) const { |
| return Get<TypeTraits<DOUBLE> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetString(int col_idx, Slice* val) const { |
| return Get<TypeTraits<STRING> >(col_idx, val); |
| } |
| |
| Status KuduScanBatch::RowPtr::GetBinary(int col_idx, Slice* val) const { |
| return Get<TypeTraits<BINARY> >(col_idx, val); |
| } |
| |
| template<typename T> |
| Status KuduScanBatch::RowPtr::Get(const Slice& col_name, typename T::cpp_type* val) const { |
| int col_idx; |
| RETURN_NOT_OK(FindColumn(*schema_, col_name, &col_idx)); |
| return Get<T>(col_idx, val); |
| } |
| |
| template<typename T> |
| Status KuduScanBatch::RowPtr::Get(int col_idx, typename T::cpp_type* val) const { |
| const ColumnSchema& col = schema_->column(col_idx); |
| if (PREDICT_FALSE(col.type_info()->type() != T::type)) { |
| // TODO: at some point we could allow type coercion here. |
| return Status::InvalidArgument( |
| Substitute("invalid type $0 provided for column '$1' (expected $2)", |
| T::name(), |
| col.name(), col.type_info()->name())); |
| } |
| |
| if (col.is_nullable() && IsNull(col_idx)) { |
| return Status::NotFound("column is NULL"); |
| } |
| |
| memcpy(val, row_data_ + schema_->column_offset(col_idx), sizeof(*val)); |
| return Status::OK(); |
| } |
| |
| const void* KuduScanBatch::RowPtr::cell(int col_idx) const { |
| return row_data_ + schema_->column_offset(col_idx); |
| } |
| |
| //------------------------------------------------------------ |
| // Template instantiations: We instantiate all possible templates to avoid linker issues. |
| // see: https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl |
| // TODO We can probably remove this when we move to c++11 and can use "extern template" |
| //------------------------------------------------------------ |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<BOOL> >(const Slice& col_name, bool* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT8> >(const Slice& col_name, int8_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT16> >(const Slice& col_name, int16_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT32> >(const Slice& col_name, int32_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT64> >(const Slice& col_name, int64_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT128> >(const Slice& col_name, int128_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<UNIXTIME_MICROS> >( |
| const Slice& col_name, int64_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<FLOAT> >(const Slice& col_name, float* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<DOUBLE> >(const Slice& col_name, double* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<STRING> >(const Slice& col_name, Slice* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<BINARY> >(const Slice& col_name, Slice* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<BOOL> >(int col_idx, bool* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT8> >(int col_idx, int8_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT16> >(int col_idx, int16_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT32> >(int col_idx, int32_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT64> >(int col_idx, int64_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<INT128> >(int col_idx, int128_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<UNIXTIME_MICROS> >(int col_idx, int64_t* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<FLOAT> >(int col_idx, float* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<DOUBLE> >(int col_idx, double* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<STRING> >(int col_idx, Slice* val) const; |
| |
| template |
| Status KuduScanBatch::RowPtr::Get<TypeTraits<BINARY> >(int col_idx, Slice* val) const; |
| |
| Status KuduScanBatch::RowPtr::GetUnscaledDecimal(int col_idx, int128_t* val) const { |
| const ColumnSchema& col = schema_->column(col_idx); |
| const DataType col_type = col.type_info()->type(); |
| switch (col_type) { |
| case DECIMAL32: |
| int32_t i32_val; |
| RETURN_NOT_OK(Get<TypeTraits<DECIMAL32> >(col_idx, &i32_val)); |
| *val = i32_val; |
| return Status::OK(); |
| case DECIMAL64: |
| int64_t i64_val; |
| RETURN_NOT_OK(Get<TypeTraits<DECIMAL64> >(col_idx, &i64_val)); |
| *val = i64_val; |
| return Status::OK(); |
| case DECIMAL128: |
| int128_t i128_val; |
| RETURN_NOT_OK(Get<TypeTraits<DECIMAL128> >(col_idx, &i128_val)); |
| *val = i128_val; |
| return Status::OK(); |
| default: |
| return Status::InvalidArgument( |
| Substitute("invalid type $0 provided for column '$1' (expected DECIMAL)", |
| col.type_info()->name(), col.name())); |
| } |
| } |
| |
| string KuduScanBatch::RowPtr::ToString() const { |
| // Client-users calling ToString() will likely expect it to not be redacted. |
| ScopedDisableRedaction no_redaction; |
| |
| string ret; |
| ret.append("("); |
| bool first = true; |
| for (int i = 0; i < schema_->num_columns(); i++) { |
| if (!first) { |
| ret.append(", "); |
| } |
| RowCell cell(this, i); |
| schema_->column(i).DebugCellAppend(cell, &ret); |
| first = false; |
| } |
| ret.append(")"); |
| return ret; |
| } |
| |
| } // namespace client |
| } // namespace kudu |