backend/datamodel/key.cc (165 lines of code) (raw):
//
// Copyright 2020 Google LLC
//
// Licensed 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 "backend/datamodel/key.h"
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "zetasql/public/numeric_value.h"
namespace google {
namespace spanner {
namespace emulator {
namespace backend {
namespace {
// Returns the logical size in bytes of the given value.
int64_t LogicalBytesInternal(const zetasql::Value& value) {
if (value.is_null()) {
return 2;
}
// TODO: Refactor this to account for physical not null vs
// logical not null keys. This causes a difference of 1 byte in the size.
switch (value.type_kind()) {
case zetasql::TYPE_BOOL:
return 1;
case zetasql::TYPE_DATE:
return 4;
case zetasql::TYPE_INT64:
case zetasql::TYPE_DOUBLE:
return 8;
case zetasql::TYPE_TIMESTAMP:
return 12;
case zetasql::TYPE_STRING:
return value.string_value().size();
case zetasql::TYPE_BYTES:
return value.bytes_value().size();
case zetasql::TYPE_NUMERIC:
// Here we use the maximal bytes of a numeric value.
return 25;
default:
// Key columns should have already been validated, so invalid key columns
// should not occur.
return 0;
}
}
} // namespace
Key::Key() = default;
Key::Key(std::vector<zetasql::Value> columns)
: columns_(std::move(columns)),
is_descending_(columns_.size()),
is_nulls_last_(columns_.size()) {}
void Key::AddColumn(zetasql::Value value, bool desc, bool is_nulls_last) {
columns_.emplace_back(std::move(value));
is_descending_.push_back(desc);
is_nulls_last_.push_back(is_nulls_last);
}
int Key::NumColumns() const { return columns_.size(); }
void Key::SetColumnValue(int i, zetasql::Value value) {
columns_[i] = std::move(value);
}
void Key::SetColumnDescending(int i, bool value) { is_descending_[i] = value; }
void Key::SetColumnNullsLast(int i, bool value) { is_nulls_last_[i] = value; }
const zetasql::Value& Key::ColumnValue(int i) const { return columns_[i]; }
bool Key::IsColumnDescending(int i) const { return is_descending_[i]; }
bool Key::IsColumnNullsLast(int i) const { return is_nulls_last_[i]; }
int Key::Compare(const Key& other) const {
// Handle infinity keys first.
if (is_infinity_ || other.is_infinity_) {
if (is_infinity_ == other.is_infinity_) {
return 0;
}
return other.is_infinity_ ? -1 : 1;
}
// Perform left-to-right column comparisons.
for (int i = 0; i < columns_.size(); ++i) {
if (i >= other.columns_.size()) {
// If we reached here, other is a prefix of *this.
return other.is_prefix_limit_ ? -1 : 1;
}
if (columns_[i].is_null() && other.columns_[i].is_null()) {
continue;
}
if (columns_[i].is_null() && !other.columns_[i].is_null()) {
return is_nulls_last_[i] ? 1 : -1;
}
if (other.columns_[i].is_null()) {
return is_nulls_last_[i] ? -1 : 1;
}
if (columns_[i].LessThan(other.columns_[i])) {
return is_descending_[i] ? 1 : -1;
}
if (columns_[i].Equals(other.columns_[i])) {
continue;
}
return is_descending_[i] ? -1 : 1;
}
// If we reached here, *this is a prefix of other.
if (other.columns_.size() > columns_.size()) {
return is_prefix_limit_ ? 1 : -1;
}
// If we reached here, all columns are equal.
if (is_prefix_limit_ != other.is_prefix_limit_) {
return is_prefix_limit_ ? 1 : -1;
}
return 0;
}
// static
Key Key::Empty() { return Key(); }
// static
Key Key::Infinity() {
Key k;
k.is_infinity_ = true;
return k;
}
Key Key::ToPrefixLimit() const {
Key k = (*this);
k.is_prefix_limit_ = true;
return k;
}
Key Key::Prefix(int n) const {
Key k = (*this);
k.columns_.resize(n);
k.is_descending_.resize(n);
return k;
}
bool Key::IsPrefixOf(const Key& other) const {
// Infinity is not a prefix of any other key except itself.
if (is_infinity_) {
return other.is_infinity_;
}
// To be a prefix, *this needs to have fewer or equal columns.
if (columns_.size() > other.columns_.size()) {
return false;
}
// If any columns of *this mismatch, *this is not a prefix.
for (int i = 0; i < columns_.size(); ++i) {
if (columns_[i] != other.columns_[i]) {
return false;
}
}
// If we reached here, all columns in *this match.
return is_prefix_limit_ == other.is_prefix_limit_;
}
int64_t Key::LogicalSizeInBytes() const {
int64_t key_size = 0;
for (int i = 0; i < NumColumns(); ++i) {
key_size += LogicalBytesInternal(ColumnValue(i));
}
return key_size;
}
std::string Key::DebugString() const {
std::stringstream out;
out << (*this);
return out.str();
}
std::ostream& operator<<(std::ostream& out, const Key& k) {
if (k.is_infinity_) {
out << "{∞}";
return out;
}
out << "{";
for (int i = 0; i < k.NumColumns(); ++i) {
if (i > 0) {
out << ", ";
}
out << k.ColumnValue(i);
if (k.is_descending_[i]) {
out << "↓";
}
}
out << "}";
if (k.is_prefix_limit_) {
out << "+";
}
return out;
}
bool operator<(const Key& k1, const Key& k2) { return k1.Compare(k2) < 0; }
bool operator<=(const Key& k1, const Key& k2) { return k1.Compare(k2) <= 0; }
bool operator==(const Key& k1, const Key& k2) { return k1.Compare(k2) == 0; }
bool operator>(const Key& k1, const Key& k2) { return k2 < k1; }
bool operator>=(const Key& k1, const Key& k2) { return k2 <= k1; }
} // namespace backend
} // namespace emulator
} // namespace spanner
} // namespace google