| /* |
| 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 "result_response.hpp" |
| |
| #include "external.hpp" |
| #include "logger.hpp" |
| #include "protocol.hpp" |
| #include "result_metadata.hpp" |
| #include "result_response.hpp" |
| #include "serialization.hpp" |
| |
| using namespace datastax; |
| using namespace datastax::internal; |
| using namespace datastax::internal::core; |
| |
| extern "C" { |
| |
| void cass_result_free(const CassResult* result) { result->dec_ref(); } |
| |
| size_t cass_result_row_count(const CassResult* result) { |
| if (result->kind() == CASS_RESULT_KIND_ROWS) { |
| return result->row_count(); |
| } |
| return 0; |
| } |
| |
| size_t cass_result_column_count(const CassResult* result) { |
| if (result->kind() == CASS_RESULT_KIND_ROWS) { |
| return result->column_count(); |
| } |
| return 0; |
| } |
| |
| CassError cass_result_column_name(const CassResult* result, size_t index, const char** name, |
| size_t* name_length) { |
| const SharedRefPtr<ResultMetadata>& metadata(result->metadata()); |
| if (index >= metadata->column_count()) { |
| return CASS_ERROR_LIB_INDEX_OUT_OF_BOUNDS; |
| } |
| if (result->kind() != CASS_RESULT_KIND_ROWS) { |
| return CASS_ERROR_LIB_BAD_PARAMS; |
| } |
| const ColumnDefinition def = metadata->get_column_definition(index); |
| *name = def.name.data(); |
| *name_length = def.name.size(); |
| return CASS_OK; |
| } |
| |
| CassValueType cass_result_column_type(const CassResult* result, size_t index) { |
| const SharedRefPtr<ResultMetadata>& metadata(result->metadata()); |
| if (result->kind() == CASS_RESULT_KIND_ROWS && index < metadata->column_count()) { |
| return metadata->get_column_definition(index).data_type->value_type(); |
| } |
| return CASS_VALUE_TYPE_UNKNOWN; |
| } |
| |
| const CassDataType* cass_result_column_data_type(const CassResult* result, size_t index) { |
| const SharedRefPtr<ResultMetadata>& metadata(result->metadata()); |
| if (result->kind() == CASS_RESULT_KIND_ROWS && index < metadata->column_count()) { |
| return CassDataType::to(metadata->get_column_definition(index).data_type.get()); |
| } |
| return NULL; |
| } |
| |
| const CassRow* cass_result_first_row(const CassResult* result) { |
| if (result->kind() == CASS_RESULT_KIND_ROWS && result->row_count() > 0) { |
| return CassRow::to(&result->first_row()); |
| } |
| return NULL; |
| } |
| |
| cass_bool_t cass_result_has_more_pages(const CassResult* result) { |
| return static_cast<cass_bool_t>(result->has_more_pages()); |
| } |
| |
| CassError cass_result_paging_state_token(const CassResult* result, const char** paging_state, |
| size_t* paging_state_size) { |
| if (!result->has_more_pages()) { |
| return CASS_ERROR_LIB_NO_PAGING_STATE; |
| } |
| *paging_state = result->paging_state().data(); |
| *paging_state_size = result->paging_state().size(); |
| return CASS_OK; |
| } |
| |
| } // extern "C" |
| |
| class DataTypeDecoder { |
| public: |
| DataTypeDecoder(Decoder& decoder, SimpleDataTypeCache& cache) |
| : decoder_(decoder) |
| , cache_(cache) {} |
| |
| DataType::ConstPtr decode() { |
| decoder_.set_type("data type"); |
| uint16_t value_type; |
| if (!decoder_.decode_uint16(value_type)) return DataType::NIL; |
| |
| switch (value_type) { |
| case CASS_VALUE_TYPE_CUSTOM: |
| return decode_custom(); |
| |
| case CASS_VALUE_TYPE_LIST: |
| case CASS_VALUE_TYPE_SET: |
| case CASS_VALUE_TYPE_MAP: |
| return decode_collection(static_cast<CassValueType>(value_type)); |
| |
| case CASS_VALUE_TYPE_UDT: |
| return decode_user_type(); |
| |
| case CASS_VALUE_TYPE_TUPLE: |
| return decode_tuple(); |
| |
| default: |
| return cache_.by_value_type(value_type); |
| } |
| } |
| |
| private: |
| DataType::ConstPtr decode_custom() { |
| StringRef class_name; |
| if (!decoder_.decode_string(&class_name)) return DataType::NIL; |
| |
| DataType::ConstPtr type = cache_.by_class(class_name); |
| if (type) return type; |
| |
| // If no mapping exists, return an actual custom type. |
| return DataType::ConstPtr(new CustomType(class_name.to_string())); |
| } |
| |
| DataType::ConstPtr decode_collection(CassValueType collection_type) { |
| DataType::Vec types; |
| types.push_back(decode()); |
| if (collection_type == CASS_VALUE_TYPE_MAP) { |
| types.push_back(decode()); |
| } |
| return DataType::ConstPtr(new CollectionType(collection_type, types, false)); |
| } |
| |
| DataType::ConstPtr decode_user_type() { |
| StringRef keyspace; |
| if (!decoder_.decode_string(&keyspace)) return DataType::NIL; |
| |
| StringRef type_name; |
| if (!decoder_.decode_string(&type_name)) return DataType::NIL; |
| |
| uint16_t n; |
| if (!decoder_.decode_uint16(n)) return DataType::NIL; |
| |
| UserType::FieldVec fields; |
| for (uint16_t i = 0; i < n; ++i) { |
| StringRef field_name; |
| if (!decoder_.decode_string(&field_name)) return DataType::NIL; |
| fields.push_back(UserType::Field(field_name.to_string(), decode())); |
| } |
| return DataType::ConstPtr( |
| new UserType(keyspace.to_string(), type_name.to_string(), fields, false)); |
| } |
| |
| DataType::ConstPtr decode_tuple() { |
| uint16_t n; |
| if (!decoder_.decode_uint16(n)) return DataType::NIL; |
| |
| DataType::Vec types; |
| for (uint16_t i = 0; i < n; ++i) { |
| types.push_back(decode()); |
| } |
| return DataType::ConstPtr(new TupleType(types, false)); |
| } |
| |
| private: |
| Decoder& decoder_; |
| SimpleDataTypeCache& cache_; |
| }; |
| |
| void ResultResponse::set_metadata(const ResultMetadata::Ptr& metadata) { |
| metadata_ = metadata; |
| decode_first_row(); |
| } |
| |
| bool ResultResponse::decode(Decoder& decoder) { |
| protocol_version_ = decoder.protocol_version(); |
| decoder.set_type("result"); |
| bool is_valid = false; |
| |
| CHECK_RESULT(decoder.decode_int32(kind_)); |
| |
| switch (kind_) { |
| case CASS_RESULT_KIND_VOID: |
| is_valid = true; |
| break; |
| |
| case CASS_RESULT_KIND_ROWS: |
| is_valid = decode_rows(decoder); |
| break; |
| |
| case CASS_RESULT_KIND_SET_KEYSPACE: |
| is_valid = decode_set_keyspace(decoder); |
| break; |
| |
| case CASS_RESULT_KIND_PREPARED: |
| is_valid = decode_prepared(decoder); |
| break; |
| |
| case CASS_RESULT_KIND_SCHEMA_CHANGE: |
| is_valid = decode_schema_change(decoder); |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| if (!is_valid) decoder.maybe_log_remaining(); |
| return is_valid; |
| } |
| |
| bool ResultResponse::decode_metadata(Decoder& decoder, ResultMetadata::Ptr* metadata, |
| bool has_pk_indices) { |
| int32_t flags = 0; |
| CHECK_RESULT(decoder.decode_int32(flags)); |
| |
| int32_t column_count = 0; |
| CHECK_RESULT(decoder.decode_int32(column_count)); |
| |
| if (flags & CASS_RESULT_FLAG_METADATA_CHANGED) { |
| if (decoder.protocol_version().supports_result_metadata_id()) { |
| CHECK_RESULT(decoder.decode_string(&new_metadata_id_)) |
| } else { |
| LOG_ERROR("Metadata changed flag set with invalid protocol version %s", |
| decoder.protocol_version().to_string().c_str()); |
| return false; |
| } |
| } |
| |
| if (has_pk_indices) { |
| int32_t pk_count = 0; |
| CHECK_RESULT(decoder.decode_int32(pk_count)); |
| for (int i = 0; i < pk_count; ++i) { |
| uint16_t pk_index = 0; |
| CHECK_RESULT(decoder.decode_uint16(pk_index)); |
| pk_indices_.push_back(pk_index); |
| } |
| } |
| |
| if (flags & CASS_RESULT_FLAG_HAS_MORE_PAGES) { |
| has_more_pages_ = true; |
| CHECK_RESULT(decoder.decode_bytes(&paging_state_)); |
| } else { |
| has_more_pages_ = false; |
| } |
| |
| if (!(flags & CASS_RESULT_FLAG_NO_METADATA)) { |
| bool global_table_spec = flags & CASS_RESULT_FLAG_GLOBAL_TABLESPEC; |
| |
| if (global_table_spec) { |
| CHECK_RESULT(decoder.decode_string(&keyspace_)); |
| CHECK_RESULT(decoder.decode_string(&table_)); |
| } |
| |
| metadata->reset(new ResultMetadata(column_count, this->buffer())); |
| |
| SimpleDataTypeCache cache; |
| |
| for (int i = 0; i < column_count; ++i) { |
| ColumnDefinition def; |
| |
| def.index = i; |
| |
| if (!global_table_spec) { |
| CHECK_RESULT(decoder.decode_string(&def.keyspace)); |
| CHECK_RESULT(decoder.decode_string(&def.table)); |
| } |
| |
| CHECK_RESULT(decoder.decode_string(&def.name)); |
| |
| DataTypeDecoder type_decoder(decoder, cache); |
| def.data_type = DataType::ConstPtr(type_decoder.decode()); |
| if (def.data_type == DataType::NIL) return false; |
| |
| (*metadata)->add(def); |
| } |
| } |
| return true; |
| } |
| |
| bool ResultResponse::decode_first_row() { |
| if (row_count_ > 0 && metadata_ && // Valid metadata required for column count |
| first_row_.values.empty()) { // Only decode the first row once |
| first_row_.values.reserve(column_count()); |
| return decode_row(row_decoder_, this, first_row_.values); |
| } |
| return true; |
| } |
| |
| bool ResultResponse::decode_rows(Decoder& decoder) { |
| CHECK_RESULT(decode_metadata(decoder, &metadata_)); |
| CHECK_RESULT(decoder.decode_int32(row_count_)); |
| row_decoder_ = decoder; |
| CHECK_RESULT(decode_first_row()); |
| return true; |
| } |
| |
| bool ResultResponse::decode_set_keyspace(Decoder& decoder) { |
| CHECK_RESULT(decoder.decode_string(&keyspace_)); |
| return true; |
| } |
| |
| bool ResultResponse::decode_prepared(Decoder& decoder) { |
| CHECK_RESULT(decoder.decode_string(&prepared_id_)); |
| if (decoder.protocol_version().supports_result_metadata_id()) { |
| CHECK_RESULT(decoder.decode_string(&result_metadata_id_)); |
| } |
| CHECK_RESULT( |
| decode_metadata(decoder, &metadata_, decoder.protocol_version() >= CASS_PROTOCOL_VERSION_V4)); |
| CHECK_RESULT(decode_metadata(decoder, &result_metadata_)); |
| return true; |
| } |
| |
| bool ResultResponse::decode_schema_change(Decoder& decoder) { |
| CHECK_RESULT(decoder.decode_string(&change_)); |
| CHECK_RESULT(decoder.decode_string(&keyspace_)); |
| CHECK_RESULT(decoder.decode_string(&table_)); |
| return true; |
| } |