blob: 04815f6be7ed3166eeb3e785f97284101441f513 [file] [log] [blame]
/*
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;
}