Merge branch 'master' into enable-experimental-indices-and-make-settings-obsolete

This commit is contained in:
Alexey Milovidov 2019-12-27 21:12:30 +03:00
commit cb3a5b26f7
90 changed files with 2883 additions and 4931 deletions

View File

@ -102,10 +102,16 @@ add_subdirectory(${FLATBUFFERS_SRC_DIR} "${FLATBUFFERS_BINARY_DIR}")
set(ARROW_IPC_SRC_DIR ${ARROW_SRC_DIR}/arrow/ipc) set(ARROW_IPC_SRC_DIR ${ARROW_SRC_DIR}/arrow/ipc)
set(ARROW_FORMAT_SRC_DIR ${ARROW_SRC_DIR}/../../format) set(ARROW_FORMAT_SRC_DIR ${ARROW_SRC_DIR}/../../format)
set(FLATBUFFERS_COMPILED_OUT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src/arrow/ipc) set(ARROW_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/arrow_gen_headers)
set(FLATBUFFERS_COMPILED_OUT_DIR ${ARROW_GENERATED_INCLUDE_DIR}/arrow/ipc)
set(FBS_OUTPUT_FILES "${FLATBUFFERS_COMPILED_OUT_DIR}/File_generated.h" "${FLATBUFFERS_COMPILED_OUT_DIR}/Message_generated.h" set(FBS_OUTPUT_FILES
"${FLATBUFFERS_COMPILED_OUT_DIR}/feather_generated.h") "${FLATBUFFERS_COMPILED_OUT_DIR}/File_generated.h"
"${FLATBUFFERS_COMPILED_OUT_DIR}/Message_generated.h"
"${FLATBUFFERS_COMPILED_OUT_DIR}/feather_generated.h"
"${FLATBUFFERS_COMPILED_OUT_DIR}/Schema_generated.h"
"${FLATBUFFERS_COMPILED_OUT_DIR}/SparseTensor_generated.h"
"${FLATBUFFERS_COMPILED_OUT_DIR}/Tensor_generated.h")
set(FBS_SRC set(FBS_SRC
${ARROW_FORMAT_SRC_DIR}/Message.fbs ${ARROW_FORMAT_SRC_DIR}/Message.fbs
@ -137,7 +143,7 @@ add_custom_command(OUTPUT ${FBS_OUTPUT_FILES}
add_custom_target(metadata_fbs DEPENDS ${FBS_OUTPUT_FILES}) add_custom_target(metadata_fbs DEPENDS ${FBS_OUTPUT_FILES})
add_dependencies(metadata_fbs flatc) add_dependencies(metadata_fbs flatc)
# arrow-cmake cmake file calling orc cmake subroutine which detects certain compiler features. # arrow-cmake cmake file calling orc cmake subroutine which detects certain compiler features.
# Apple Clang compiler failed to compile this code without specifying c++11 standard. # Apple Clang compiler failed to compile this code without specifying c++11 standard.
# As result these compiler features detected as absent. In result it failed to compile orc itself. # As result these compiler features detected as absent. In result it failed to compile orc itself.
# In orc makefile there is code that sets flags, but arrow-cmake ignores these flags. # In orc makefile there is code that sets flags, but arrow-cmake ignores these flags.
@ -231,12 +237,10 @@ set(ARROW_SRCS
${LIBRARY_DIR}/ipc/dictionary.cc ${LIBRARY_DIR}/ipc/dictionary.cc
${LIBRARY_DIR}/ipc/feather.cc ${LIBRARY_DIR}/ipc/feather.cc
# ${LIBRARY_DIR}/ipc/file_to_stream.cc
${LIBRARY_DIR}/ipc/message.cc ${LIBRARY_DIR}/ipc/message.cc
${LIBRARY_DIR}/ipc/metadata_internal.cc ${LIBRARY_DIR}/ipc/metadata_internal.cc
${LIBRARY_DIR}/ipc/options.cc ${LIBRARY_DIR}/ipc/options.cc
${LIBRARY_DIR}/ipc/reader.cc ${LIBRARY_DIR}/ipc/reader.cc
# ${LIBRARY_DIR}/ipc/stream_to_file.cc
${LIBRARY_DIR}/ipc/writer.cc ${LIBRARY_DIR}/ipc/writer.cc
${LIBRARY_DIR}/io/buffered.cc ${LIBRARY_DIR}/io/buffered.cc
@ -249,8 +253,6 @@ set(ARROW_SRCS
${LIBRARY_DIR}/util/basic_decimal.cc ${LIBRARY_DIR}/util/basic_decimal.cc
${LIBRARY_DIR}/util/bit_util.cc ${LIBRARY_DIR}/util/bit_util.cc
# ${LIBRARY_DIR}/util/compression_brotli.cc
# ${LIBRARY_DIR}/util/compression_bz2.cc
${LIBRARY_DIR}/util/compression.cc ${LIBRARY_DIR}/util/compression.cc
${LIBRARY_DIR}/util/compression_lz4.cc ${LIBRARY_DIR}/util/compression_lz4.cc
${LIBRARY_DIR}/util/compression_snappy.cc ${LIBRARY_DIR}/util/compression_snappy.cc
@ -268,7 +270,6 @@ set(ARROW_SRCS
${LIBRARY_DIR}/util/task_group.cc ${LIBRARY_DIR}/util/task_group.cc
${LIBRARY_DIR}/util/thread_pool.cc ${LIBRARY_DIR}/util/thread_pool.cc
${LIBRARY_DIR}/util/trie.cc ${LIBRARY_DIR}/util/trie.cc
# ${LIBRARY_DIR}/util/uri.cc
${LIBRARY_DIR}/util/utf8.cc ${LIBRARY_DIR}/util/utf8.cc
${LIBRARY_DIR}/vendored/base64.cpp ${LIBRARY_DIR}/vendored/base64.cpp
@ -356,6 +357,8 @@ target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ORC_BUILD_INCLUDE_D
target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ORC_ADDITION_SOURCE_DIR}) target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ORC_ADDITION_SOURCE_DIR})
target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ARROW_SRC_DIR}) target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ARROW_SRC_DIR})
target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${FLATBUFFERS_INCLUDE_DIR}) target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${FLATBUFFERS_INCLUDE_DIR})
target_include_directories(${ARROW_LIBRARY} PRIVATE SYSTEM ${ARROW_GENERATED_INCLUDE_DIR})
# === parquet # === parquet
set(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src/parquet) set(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src/parquet)

View File

@ -1,181 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_FILE_ORG_APACHE_ARROW_FLATBUF_H_
#define FLATBUFFERS_GENERATED_FILE_ORG_APACHE_ARROW_FLATBUF_H_
#include "flatbuffers/flatbuffers.h"
#include "Schema_generated.h"
namespace org {
namespace apache {
namespace arrow {
namespace flatbuf {
struct Footer;
struct Block;
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Block FLATBUFFERS_FINAL_CLASS {
private:
int64_t offset_;
int32_t metaDataLength_;
int32_t padding0__;
int64_t bodyLength_;
public:
Block() {
memset(static_cast<void *>(this), 0, sizeof(Block));
}
Block(int64_t _offset, int32_t _metaDataLength, int64_t _bodyLength)
: offset_(flatbuffers::EndianScalar(_offset)),
metaDataLength_(flatbuffers::EndianScalar(_metaDataLength)),
padding0__(0),
bodyLength_(flatbuffers::EndianScalar(_bodyLength)) {
(void)padding0__;
}
/// Index to the start of the RecordBlock (note this is past the Message header)
int64_t offset() const {
return flatbuffers::EndianScalar(offset_);
}
/// Length of the metadata
int32_t metaDataLength() const {
return flatbuffers::EndianScalar(metaDataLength_);
}
/// Length of the data (this is aligned so there can be a gap between this and
/// the metatdata).
int64_t bodyLength() const {
return flatbuffers::EndianScalar(bodyLength_);
}
};
FLATBUFFERS_STRUCT_END(Block, 24);
/// ----------------------------------------------------------------------
/// Arrow File metadata
///
struct Footer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_VERSION = 4,
VT_SCHEMA = 6,
VT_DICTIONARIES = 8,
VT_RECORDBATCHES = 10
};
MetadataVersion version() const {
return static_cast<MetadataVersion>(GetField<int16_t>(VT_VERSION, 0));
}
const Schema *schema() const {
return GetPointer<const Schema *>(VT_SCHEMA);
}
const flatbuffers::Vector<const Block *> *dictionaries() const {
return GetPointer<const flatbuffers::Vector<const Block *> *>(VT_DICTIONARIES);
}
const flatbuffers::Vector<const Block *> *recordBatches() const {
return GetPointer<const flatbuffers::Vector<const Block *> *>(VT_RECORDBATCHES);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int16_t>(verifier, VT_VERSION) &&
VerifyOffset(verifier, VT_SCHEMA) &&
verifier.VerifyTable(schema()) &&
VerifyOffset(verifier, VT_DICTIONARIES) &&
verifier.VerifyVector(dictionaries()) &&
VerifyOffset(verifier, VT_RECORDBATCHES) &&
verifier.VerifyVector(recordBatches()) &&
verifier.EndTable();
}
};
struct FooterBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_version(MetadataVersion version) {
fbb_.AddElement<int16_t>(Footer::VT_VERSION, static_cast<int16_t>(version), 0);
}
void add_schema(flatbuffers::Offset<Schema> schema) {
fbb_.AddOffset(Footer::VT_SCHEMA, schema);
}
void add_dictionaries(flatbuffers::Offset<flatbuffers::Vector<const Block *>> dictionaries) {
fbb_.AddOffset(Footer::VT_DICTIONARIES, dictionaries);
}
void add_recordBatches(flatbuffers::Offset<flatbuffers::Vector<const Block *>> recordBatches) {
fbb_.AddOffset(Footer::VT_RECORDBATCHES, recordBatches);
}
explicit FooterBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
FooterBuilder &operator=(const FooterBuilder &);
flatbuffers::Offset<Footer> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Footer>(end);
return o;
}
};
inline flatbuffers::Offset<Footer> CreateFooter(
flatbuffers::FlatBufferBuilder &_fbb,
MetadataVersion version = MetadataVersion_V1,
flatbuffers::Offset<Schema> schema = 0,
flatbuffers::Offset<flatbuffers::Vector<const Block *>> dictionaries = 0,
flatbuffers::Offset<flatbuffers::Vector<const Block *>> recordBatches = 0) {
FooterBuilder builder_(_fbb);
builder_.add_recordBatches(recordBatches);
builder_.add_dictionaries(dictionaries);
builder_.add_schema(schema);
builder_.add_version(version);
return builder_.Finish();
}
inline flatbuffers::Offset<Footer> CreateFooterDirect(
flatbuffers::FlatBufferBuilder &_fbb,
MetadataVersion version = MetadataVersion_V1,
flatbuffers::Offset<Schema> schema = 0,
const std::vector<Block> *dictionaries = nullptr,
const std::vector<Block> *recordBatches = nullptr) {
auto dictionaries__ = dictionaries ? _fbb.CreateVectorOfStructs<Block>(*dictionaries) : 0;
auto recordBatches__ = recordBatches ? _fbb.CreateVectorOfStructs<Block>(*recordBatches) : 0;
return org::apache::arrow::flatbuf::CreateFooter(
_fbb,
version,
schema,
dictionaries__,
recordBatches__);
}
inline const org::apache::arrow::flatbuf::Footer *GetFooter(const void *buf) {
return flatbuffers::GetRoot<org::apache::arrow::flatbuf::Footer>(buf);
}
inline const org::apache::arrow::flatbuf::Footer *GetSizePrefixedFooter(const void *buf) {
return flatbuffers::GetSizePrefixedRoot<org::apache::arrow::flatbuf::Footer>(buf);
}
inline bool VerifyFooterBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<org::apache::arrow::flatbuf::Footer>(nullptr);
}
inline bool VerifySizePrefixedFooterBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<org::apache::arrow::flatbuf::Footer>(nullptr);
}
inline void FinishFooterBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Footer> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedFooterBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Footer> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace flatbuf
} // namespace arrow
} // namespace apache
} // namespace org
#endif // FLATBUFFERS_GENERATED_FILE_ORG_APACHE_ARROW_FLATBUF_H_

View File

@ -1,508 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_MESSAGE_ORG_APACHE_ARROW_FLATBUF_H_
#define FLATBUFFERS_GENERATED_MESSAGE_ORG_APACHE_ARROW_FLATBUF_H_
#include "flatbuffers/flatbuffers.h"
#include "Schema_generated.h"
#include "SparseTensor_generated.h"
#include "Tensor_generated.h"
namespace org {
namespace apache {
namespace arrow {
namespace flatbuf {
struct FieldNode;
struct RecordBatch;
struct DictionaryBatch;
struct Message;
/// ----------------------------------------------------------------------
/// The root Message type
/// This union enables us to easily send different message types without
/// redundant storage, and in the future we can easily add new message types.
///
/// Arrow implementations do not need to implement all of the message types,
/// which may include experimental metadata types. For maximum compatibility,
/// it is best to send data using RecordBatch
enum MessageHeader {
MessageHeader_NONE = 0,
MessageHeader_Schema = 1,
MessageHeader_DictionaryBatch = 2,
MessageHeader_RecordBatch = 3,
MessageHeader_Tensor = 4,
MessageHeader_SparseTensor = 5,
MessageHeader_MIN = MessageHeader_NONE,
MessageHeader_MAX = MessageHeader_SparseTensor
};
inline const MessageHeader (&EnumValuesMessageHeader())[6] {
static const MessageHeader values[] = {
MessageHeader_NONE,
MessageHeader_Schema,
MessageHeader_DictionaryBatch,
MessageHeader_RecordBatch,
MessageHeader_Tensor,
MessageHeader_SparseTensor
};
return values;
}
inline const char * const *EnumNamesMessageHeader() {
static const char * const names[] = {
"NONE",
"Schema",
"DictionaryBatch",
"RecordBatch",
"Tensor",
"SparseTensor",
nullptr
};
return names;
}
inline const char *EnumNameMessageHeader(MessageHeader e) {
if (e < MessageHeader_NONE || e > MessageHeader_SparseTensor) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesMessageHeader()[index];
}
template<typename T> struct MessageHeaderTraits {
static const MessageHeader enum_value = MessageHeader_NONE;
};
template<> struct MessageHeaderTraits<Schema> {
static const MessageHeader enum_value = MessageHeader_Schema;
};
template<> struct MessageHeaderTraits<DictionaryBatch> {
static const MessageHeader enum_value = MessageHeader_DictionaryBatch;
};
template<> struct MessageHeaderTraits<RecordBatch> {
static const MessageHeader enum_value = MessageHeader_RecordBatch;
};
template<> struct MessageHeaderTraits<Tensor> {
static const MessageHeader enum_value = MessageHeader_Tensor;
};
template<> struct MessageHeaderTraits<SparseTensor> {
static const MessageHeader enum_value = MessageHeader_SparseTensor;
};
bool VerifyMessageHeader(flatbuffers::Verifier &verifier, const void *obj, MessageHeader type);
bool VerifyMessageHeaderVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
/// ----------------------------------------------------------------------
/// Data structures for describing a table row batch (a collection of
/// equal-length Arrow arrays)
/// Metadata about a field at some level of a nested type tree (but not
/// its children).
///
/// For example, a List<Int16> with values [[1, 2, 3], null, [4], [5, 6], null]
/// would have {length: 5, null_count: 2} for its List node, and {length: 6,
/// null_count: 0} for its Int16 node, as separate FieldNode structs
FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) FieldNode FLATBUFFERS_FINAL_CLASS {
private:
int64_t length_;
int64_t null_count_;
public:
FieldNode() {
memset(static_cast<void *>(this), 0, sizeof(FieldNode));
}
FieldNode(int64_t _length, int64_t _null_count)
: length_(flatbuffers::EndianScalar(_length)),
null_count_(flatbuffers::EndianScalar(_null_count)) {
}
/// The number of value slots in the Arrow array at this level of a nested
/// tree
int64_t length() const {
return flatbuffers::EndianScalar(length_);
}
/// The number of observed nulls. Fields with null_count == 0 may choose not
/// to write their physical validity bitmap out as a materialized buffer,
/// instead setting the length of the bitmap buffer to 0.
int64_t null_count() const {
return flatbuffers::EndianScalar(null_count_);
}
};
FLATBUFFERS_STRUCT_END(FieldNode, 16);
/// A data header describing the shared memory layout of a "record" or "row"
/// batch. Some systems call this a "row batch" internally and others a "record
/// batch".
struct RecordBatch FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_LENGTH = 4,
VT_NODES = 6,
VT_BUFFERS = 8
};
/// number of records / rows. The arrays in the batch should all have this
/// length
int64_t length() const {
return GetField<int64_t>(VT_LENGTH, 0);
}
/// Nodes correspond to the pre-ordered flattened logical schema
const flatbuffers::Vector<const FieldNode *> *nodes() const {
return GetPointer<const flatbuffers::Vector<const FieldNode *> *>(VT_NODES);
}
/// Buffers correspond to the pre-ordered flattened buffer tree
///
/// The number of buffers appended to this list depends on the schema. For
/// example, most primitive arrays will have 2 buffers, 1 for the validity
/// bitmap and 1 for the values. For struct arrays, there will only be a
/// single buffer for the validity (nulls) bitmap
const flatbuffers::Vector<const Buffer *> *buffers() const {
return GetPointer<const flatbuffers::Vector<const Buffer *> *>(VT_BUFFERS);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_LENGTH) &&
VerifyOffset(verifier, VT_NODES) &&
verifier.VerifyVector(nodes()) &&
VerifyOffset(verifier, VT_BUFFERS) &&
verifier.VerifyVector(buffers()) &&
verifier.EndTable();
}
};
struct RecordBatchBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_length(int64_t length) {
fbb_.AddElement<int64_t>(RecordBatch::VT_LENGTH, length, 0);
}
void add_nodes(flatbuffers::Offset<flatbuffers::Vector<const FieldNode *>> nodes) {
fbb_.AddOffset(RecordBatch::VT_NODES, nodes);
}
void add_buffers(flatbuffers::Offset<flatbuffers::Vector<const Buffer *>> buffers) {
fbb_.AddOffset(RecordBatch::VT_BUFFERS, buffers);
}
explicit RecordBatchBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
RecordBatchBuilder &operator=(const RecordBatchBuilder &);
flatbuffers::Offset<RecordBatch> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<RecordBatch>(end);
return o;
}
};
inline flatbuffers::Offset<RecordBatch> CreateRecordBatch(
flatbuffers::FlatBufferBuilder &_fbb,
int64_t length = 0,
flatbuffers::Offset<flatbuffers::Vector<const FieldNode *>> nodes = 0,
flatbuffers::Offset<flatbuffers::Vector<const Buffer *>> buffers = 0) {
RecordBatchBuilder builder_(_fbb);
builder_.add_length(length);
builder_.add_buffers(buffers);
builder_.add_nodes(nodes);
return builder_.Finish();
}
inline flatbuffers::Offset<RecordBatch> CreateRecordBatchDirect(
flatbuffers::FlatBufferBuilder &_fbb,
int64_t length = 0,
const std::vector<FieldNode> *nodes = nullptr,
const std::vector<Buffer> *buffers = nullptr) {
auto nodes__ = nodes ? _fbb.CreateVectorOfStructs<FieldNode>(*nodes) : 0;
auto buffers__ = buffers ? _fbb.CreateVectorOfStructs<Buffer>(*buffers) : 0;
return org::apache::arrow::flatbuf::CreateRecordBatch(
_fbb,
length,
nodes__,
buffers__);
}
/// For sending dictionary encoding information. Any Field can be
/// dictionary-encoded, but in this case none of its children may be
/// dictionary-encoded.
/// There is one vector / column per dictionary, but that vector / column
/// may be spread across multiple dictionary batches by using the isDelta
/// flag
struct DictionaryBatch FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ID = 4,
VT_DATA = 6,
VT_ISDELTA = 8
};
int64_t id() const {
return GetField<int64_t>(VT_ID, 0);
}
const RecordBatch *data() const {
return GetPointer<const RecordBatch *>(VT_DATA);
}
/// If isDelta is true the values in the dictionary are to be appended to a
/// dictionary with the indicated id
bool isDelta() const {
return GetField<uint8_t>(VT_ISDELTA, 0) != 0;
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_ID) &&
VerifyOffset(verifier, VT_DATA) &&
verifier.VerifyTable(data()) &&
VerifyField<uint8_t>(verifier, VT_ISDELTA) &&
verifier.EndTable();
}
};
struct DictionaryBatchBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_id(int64_t id) {
fbb_.AddElement<int64_t>(DictionaryBatch::VT_ID, id, 0);
}
void add_data(flatbuffers::Offset<RecordBatch> data) {
fbb_.AddOffset(DictionaryBatch::VT_DATA, data);
}
void add_isDelta(bool isDelta) {
fbb_.AddElement<uint8_t>(DictionaryBatch::VT_ISDELTA, static_cast<uint8_t>(isDelta), 0);
}
explicit DictionaryBatchBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
DictionaryBatchBuilder &operator=(const DictionaryBatchBuilder &);
flatbuffers::Offset<DictionaryBatch> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<DictionaryBatch>(end);
return o;
}
};
inline flatbuffers::Offset<DictionaryBatch> CreateDictionaryBatch(
flatbuffers::FlatBufferBuilder &_fbb,
int64_t id = 0,
flatbuffers::Offset<RecordBatch> data = 0,
bool isDelta = false) {
DictionaryBatchBuilder builder_(_fbb);
builder_.add_id(id);
builder_.add_data(data);
builder_.add_isDelta(isDelta);
return builder_.Finish();
}
struct Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_VERSION = 4,
VT_HEADER_TYPE = 6,
VT_HEADER = 8,
VT_BODYLENGTH = 10,
VT_CUSTOM_METADATA = 12
};
MetadataVersion version() const {
return static_cast<MetadataVersion>(GetField<int16_t>(VT_VERSION, 0));
}
MessageHeader header_type() const {
return static_cast<MessageHeader>(GetField<uint8_t>(VT_HEADER_TYPE, 0));
}
const void *header() const {
return GetPointer<const void *>(VT_HEADER);
}
template<typename T> const T *header_as() const;
const Schema *header_as_Schema() const {
return header_type() == MessageHeader_Schema ? static_cast<const Schema *>(header()) : nullptr;
}
const DictionaryBatch *header_as_DictionaryBatch() const {
return header_type() == MessageHeader_DictionaryBatch ? static_cast<const DictionaryBatch *>(header()) : nullptr;
}
const RecordBatch *header_as_RecordBatch() const {
return header_type() == MessageHeader_RecordBatch ? static_cast<const RecordBatch *>(header()) : nullptr;
}
const Tensor *header_as_Tensor() const {
return header_type() == MessageHeader_Tensor ? static_cast<const Tensor *>(header()) : nullptr;
}
const SparseTensor *header_as_SparseTensor() const {
return header_type() == MessageHeader_SparseTensor ? static_cast<const SparseTensor *>(header()) : nullptr;
}
int64_t bodyLength() const {
return GetField<int64_t>(VT_BODYLENGTH, 0);
}
const flatbuffers::Vector<flatbuffers::Offset<KeyValue>> *custom_metadata() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<KeyValue>> *>(VT_CUSTOM_METADATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int16_t>(verifier, VT_VERSION) &&
VerifyField<uint8_t>(verifier, VT_HEADER_TYPE) &&
VerifyOffset(verifier, VT_HEADER) &&
VerifyMessageHeader(verifier, header(), header_type()) &&
VerifyField<int64_t>(verifier, VT_BODYLENGTH) &&
VerifyOffset(verifier, VT_CUSTOM_METADATA) &&
verifier.VerifyVector(custom_metadata()) &&
verifier.VerifyVectorOfTables(custom_metadata()) &&
verifier.EndTable();
}
};
template<> inline const Schema *Message::header_as<Schema>() const {
return header_as_Schema();
}
template<> inline const DictionaryBatch *Message::header_as<DictionaryBatch>() const {
return header_as_DictionaryBatch();
}
template<> inline const RecordBatch *Message::header_as<RecordBatch>() const {
return header_as_RecordBatch();
}
template<> inline const Tensor *Message::header_as<Tensor>() const {
return header_as_Tensor();
}
template<> inline const SparseTensor *Message::header_as<SparseTensor>() const {
return header_as_SparseTensor();
}
struct MessageBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_version(MetadataVersion version) {
fbb_.AddElement<int16_t>(Message::VT_VERSION, static_cast<int16_t>(version), 0);
}
void add_header_type(MessageHeader header_type) {
fbb_.AddElement<uint8_t>(Message::VT_HEADER_TYPE, static_cast<uint8_t>(header_type), 0);
}
void add_header(flatbuffers::Offset<void> header) {
fbb_.AddOffset(Message::VT_HEADER, header);
}
void add_bodyLength(int64_t bodyLength) {
fbb_.AddElement<int64_t>(Message::VT_BODYLENGTH, bodyLength, 0);
}
void add_custom_metadata(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<KeyValue>>> custom_metadata) {
fbb_.AddOffset(Message::VT_CUSTOM_METADATA, custom_metadata);
}
explicit MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
MessageBuilder &operator=(const MessageBuilder &);
flatbuffers::Offset<Message> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Message>(end);
return o;
}
};
inline flatbuffers::Offset<Message> CreateMessage(
flatbuffers::FlatBufferBuilder &_fbb,
MetadataVersion version = MetadataVersion_V1,
MessageHeader header_type = MessageHeader_NONE,
flatbuffers::Offset<void> header = 0,
int64_t bodyLength = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<KeyValue>>> custom_metadata = 0) {
MessageBuilder builder_(_fbb);
builder_.add_bodyLength(bodyLength);
builder_.add_custom_metadata(custom_metadata);
builder_.add_header(header);
builder_.add_version(version);
builder_.add_header_type(header_type);
return builder_.Finish();
}
inline flatbuffers::Offset<Message> CreateMessageDirect(
flatbuffers::FlatBufferBuilder &_fbb,
MetadataVersion version = MetadataVersion_V1,
MessageHeader header_type = MessageHeader_NONE,
flatbuffers::Offset<void> header = 0,
int64_t bodyLength = 0,
const std::vector<flatbuffers::Offset<KeyValue>> *custom_metadata = nullptr) {
auto custom_metadata__ = custom_metadata ? _fbb.CreateVector<flatbuffers::Offset<KeyValue>>(*custom_metadata) : 0;
return org::apache::arrow::flatbuf::CreateMessage(
_fbb,
version,
header_type,
header,
bodyLength,
custom_metadata__);
}
inline bool VerifyMessageHeader(flatbuffers::Verifier &verifier, const void *obj, MessageHeader type) {
switch (type) {
case MessageHeader_NONE: {
return true;
}
case MessageHeader_Schema: {
auto ptr = reinterpret_cast<const Schema *>(obj);
return verifier.VerifyTable(ptr);
}
case MessageHeader_DictionaryBatch: {
auto ptr = reinterpret_cast<const DictionaryBatch *>(obj);
return verifier.VerifyTable(ptr);
}
case MessageHeader_RecordBatch: {
auto ptr = reinterpret_cast<const RecordBatch *>(obj);
return verifier.VerifyTable(ptr);
}
case MessageHeader_Tensor: {
auto ptr = reinterpret_cast<const Tensor *>(obj);
return verifier.VerifyTable(ptr);
}
case MessageHeader_SparseTensor: {
auto ptr = reinterpret_cast<const SparseTensor *>(obj);
return verifier.VerifyTable(ptr);
}
default: return false;
}
}
inline bool VerifyMessageHeaderVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifyMessageHeader(
verifier, values->Get(i), types->GetEnum<MessageHeader>(i))) {
return false;
}
}
return true;
}
inline const org::apache::arrow::flatbuf::Message *GetMessage(const void *buf) {
return flatbuffers::GetRoot<org::apache::arrow::flatbuf::Message>(buf);
}
inline const org::apache::arrow::flatbuf::Message *GetSizePrefixedMessage(const void *buf) {
return flatbuffers::GetSizePrefixedRoot<org::apache::arrow::flatbuf::Message>(buf);
}
inline bool VerifyMessageBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<org::apache::arrow::flatbuf::Message>(nullptr);
}
inline bool VerifySizePrefixedMessageBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<org::apache::arrow::flatbuf::Message>(nullptr);
}
inline void FinishMessageBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Message> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedMessageBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Message> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace flatbuf
} // namespace arrow
} // namespace apache
} // namespace org
#endif // FLATBUFFERS_GENERATED_MESSAGE_ORG_APACHE_ARROW_FLATBUF_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,644 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_SPARSETENSOR_ORG_APACHE_ARROW_FLATBUF_H_
#define FLATBUFFERS_GENERATED_SPARSETENSOR_ORG_APACHE_ARROW_FLATBUF_H_
#include "flatbuffers/flatbuffers.h"
#include "Schema_generated.h"
#include "Tensor_generated.h"
namespace org {
namespace apache {
namespace arrow {
namespace flatbuf {
struct SparseTensorIndexCOO;
struct SparseMatrixIndexCSR;
struct SparseTensor;
enum SparseTensorIndex {
SparseTensorIndex_NONE = 0,
SparseTensorIndex_SparseTensorIndexCOO = 1,
SparseTensorIndex_SparseMatrixIndexCSR = 2,
SparseTensorIndex_MIN = SparseTensorIndex_NONE,
SparseTensorIndex_MAX = SparseTensorIndex_SparseMatrixIndexCSR
};
inline const SparseTensorIndex (&EnumValuesSparseTensorIndex())[3] {
static const SparseTensorIndex values[] = {
SparseTensorIndex_NONE,
SparseTensorIndex_SparseTensorIndexCOO,
SparseTensorIndex_SparseMatrixIndexCSR
};
return values;
}
inline const char * const *EnumNamesSparseTensorIndex() {
static const char * const names[] = {
"NONE",
"SparseTensorIndexCOO",
"SparseMatrixIndexCSR",
nullptr
};
return names;
}
inline const char *EnumNameSparseTensorIndex(SparseTensorIndex e) {
if (e < SparseTensorIndex_NONE || e > SparseTensorIndex_SparseMatrixIndexCSR) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesSparseTensorIndex()[index];
}
template<typename T> struct SparseTensorIndexTraits {
static const SparseTensorIndex enum_value = SparseTensorIndex_NONE;
};
template<> struct SparseTensorIndexTraits<SparseTensorIndexCOO> {
static const SparseTensorIndex enum_value = SparseTensorIndex_SparseTensorIndexCOO;
};
template<> struct SparseTensorIndexTraits<SparseMatrixIndexCSR> {
static const SparseTensorIndex enum_value = SparseTensorIndex_SparseMatrixIndexCSR;
};
bool VerifySparseTensorIndex(flatbuffers::Verifier &verifier, const void *obj, SparseTensorIndex type);
bool VerifySparseTensorIndexVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
/// ----------------------------------------------------------------------
/// EXPERIMENTAL: Data structures for sparse tensors
/// Coodinate (COO) format of sparse tensor index.
///
/// COO's index list are represented as a NxM matrix,
/// where N is the number of non-zero values,
/// and M is the number of dimensions of a sparse tensor.
///
/// indicesBuffer stores the location and size of the data of this indices
/// matrix. The value type and the stride of the indices matrix is
/// specified in indicesType and indicesStrides fields.
///
/// For example, let X be a 2x3x4x5 tensor, and it has the following
/// 6 non-zero values:
///
/// X[0, 1, 2, 0] := 1
/// X[1, 1, 2, 3] := 2
/// X[0, 2, 1, 0] := 3
/// X[0, 1, 3, 0] := 4
/// X[0, 1, 2, 1] := 5
/// X[1, 2, 0, 4] := 6
///
/// In COO format, the index matrix of X is the following 4x6 matrix:
///
/// [[0, 0, 0, 0, 1, 1],
/// [1, 1, 1, 2, 1, 2],
/// [2, 2, 3, 1, 2, 0],
/// [0, 1, 0, 0, 3, 4]]
///
/// Note that the indices are sorted in lexicographical order.
struct SparseTensorIndexCOO FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_INDICESTYPE = 4,
VT_INDICESSTRIDES = 6,
VT_INDICESBUFFER = 8
};
/// The type of values in indicesBuffer
const Int *indicesType() const {
return GetPointer<const Int *>(VT_INDICESTYPE);
}
/// Non-negative byte offsets to advance one value cell along each dimension
const flatbuffers::Vector<int64_t> *indicesStrides() const {
return GetPointer<const flatbuffers::Vector<int64_t> *>(VT_INDICESSTRIDES);
}
/// The location and size of the indices matrix's data
const Buffer *indicesBuffer() const {
return GetStruct<const Buffer *>(VT_INDICESBUFFER);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_INDICESTYPE) &&
verifier.VerifyTable(indicesType()) &&
VerifyOffset(verifier, VT_INDICESSTRIDES) &&
verifier.VerifyVector(indicesStrides()) &&
VerifyField<Buffer>(verifier, VT_INDICESBUFFER) &&
verifier.EndTable();
}
};
struct SparseTensorIndexCOOBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_indicesType(flatbuffers::Offset<Int> indicesType) {
fbb_.AddOffset(SparseTensorIndexCOO::VT_INDICESTYPE, indicesType);
}
void add_indicesStrides(flatbuffers::Offset<flatbuffers::Vector<int64_t>> indicesStrides) {
fbb_.AddOffset(SparseTensorIndexCOO::VT_INDICESSTRIDES, indicesStrides);
}
void add_indicesBuffer(const Buffer *indicesBuffer) {
fbb_.AddStruct(SparseTensorIndexCOO::VT_INDICESBUFFER, indicesBuffer);
}
explicit SparseTensorIndexCOOBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
SparseTensorIndexCOOBuilder &operator=(const SparseTensorIndexCOOBuilder &);
flatbuffers::Offset<SparseTensorIndexCOO> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<SparseTensorIndexCOO>(end);
return o;
}
};
inline flatbuffers::Offset<SparseTensorIndexCOO> CreateSparseTensorIndexCOO(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<Int> indicesType = 0,
flatbuffers::Offset<flatbuffers::Vector<int64_t>> indicesStrides = 0,
const Buffer *indicesBuffer = 0) {
SparseTensorIndexCOOBuilder builder_(_fbb);
builder_.add_indicesBuffer(indicesBuffer);
builder_.add_indicesStrides(indicesStrides);
builder_.add_indicesType(indicesType);
return builder_.Finish();
}
inline flatbuffers::Offset<SparseTensorIndexCOO> CreateSparseTensorIndexCOODirect(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<Int> indicesType = 0,
const std::vector<int64_t> *indicesStrides = nullptr,
const Buffer *indicesBuffer = 0) {
auto indicesStrides__ = indicesStrides ? _fbb.CreateVector<int64_t>(*indicesStrides) : 0;
return org::apache::arrow::flatbuf::CreateSparseTensorIndexCOO(
_fbb,
indicesType,
indicesStrides__,
indicesBuffer);
}
/// Compressed Sparse Row format, that is matrix-specific.
struct SparseMatrixIndexCSR FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_INDPTRTYPE = 4,
VT_INDPTRBUFFER = 6,
VT_INDICESTYPE = 8,
VT_INDICESBUFFER = 10
};
/// The type of values in indptrBuffer
const Int *indptrType() const {
return GetPointer<const Int *>(VT_INDPTRTYPE);
}
/// indptrBuffer stores the location and size of indptr array that
/// represents the range of the rows.
/// The i-th row spans from indptr[i] to indptr[i+1] in the data.
/// The length of this array is 1 + (the number of rows), and the type
/// of index value is long.
///
/// For example, let X be the following 6x4 matrix:
///
/// X := [[0, 1, 2, 0],
/// [0, 0, 3, 0],
/// [0, 4, 0, 5],
/// [0, 0, 0, 0],
/// [6, 0, 7, 8],
/// [0, 9, 0, 0]].
///
/// The array of non-zero values in X is:
///
/// values(X) = [1, 2, 3, 4, 5, 6, 7, 8, 9].
///
/// And the indptr of X is:
///
/// indptr(X) = [0, 2, 3, 5, 5, 8, 10].
const Buffer *indptrBuffer() const {
return GetStruct<const Buffer *>(VT_INDPTRBUFFER);
}
/// The type of values in indicesBuffer
const Int *indicesType() const {
return GetPointer<const Int *>(VT_INDICESTYPE);
}
/// indicesBuffer stores the location and size of the array that
/// contains the column indices of the corresponding non-zero values.
/// The type of index value is long.
///
/// For example, the indices of the above X is:
///
/// indices(X) = [1, 2, 2, 1, 3, 0, 2, 3, 1].
///
/// Note that the indices are sorted in lexicographical order for each row.
const Buffer *indicesBuffer() const {
return GetStruct<const Buffer *>(VT_INDICESBUFFER);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_INDPTRTYPE) &&
verifier.VerifyTable(indptrType()) &&
VerifyField<Buffer>(verifier, VT_INDPTRBUFFER) &&
VerifyOffset(verifier, VT_INDICESTYPE) &&
verifier.VerifyTable(indicesType()) &&
VerifyField<Buffer>(verifier, VT_INDICESBUFFER) &&
verifier.EndTable();
}
};
struct SparseMatrixIndexCSRBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_indptrType(flatbuffers::Offset<Int> indptrType) {
fbb_.AddOffset(SparseMatrixIndexCSR::VT_INDPTRTYPE, indptrType);
}
void add_indptrBuffer(const Buffer *indptrBuffer) {
fbb_.AddStruct(SparseMatrixIndexCSR::VT_INDPTRBUFFER, indptrBuffer);
}
void add_indicesType(flatbuffers::Offset<Int> indicesType) {
fbb_.AddOffset(SparseMatrixIndexCSR::VT_INDICESTYPE, indicesType);
}
void add_indicesBuffer(const Buffer *indicesBuffer) {
fbb_.AddStruct(SparseMatrixIndexCSR::VT_INDICESBUFFER, indicesBuffer);
}
explicit SparseMatrixIndexCSRBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
SparseMatrixIndexCSRBuilder &operator=(const SparseMatrixIndexCSRBuilder &);
flatbuffers::Offset<SparseMatrixIndexCSR> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<SparseMatrixIndexCSR>(end);
return o;
}
};
inline flatbuffers::Offset<SparseMatrixIndexCSR> CreateSparseMatrixIndexCSR(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<Int> indptrType = 0,
const Buffer *indptrBuffer = 0,
flatbuffers::Offset<Int> indicesType = 0,
const Buffer *indicesBuffer = 0) {
SparseMatrixIndexCSRBuilder builder_(_fbb);
builder_.add_indicesBuffer(indicesBuffer);
builder_.add_indicesType(indicesType);
builder_.add_indptrBuffer(indptrBuffer);
builder_.add_indptrType(indptrType);
return builder_.Finish();
}
struct SparseTensor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_TYPE_TYPE = 4,
VT_TYPE = 6,
VT_SHAPE = 8,
VT_NON_ZERO_LENGTH = 10,
VT_SPARSEINDEX_TYPE = 12,
VT_SPARSEINDEX = 14,
VT_DATA = 16
};
Type type_type() const {
return static_cast<Type>(GetField<uint8_t>(VT_TYPE_TYPE, 0));
}
/// The type of data contained in a value cell.
/// Currently only fixed-width value types are supported,
/// no strings or nested types.
const void *type() const {
return GetPointer<const void *>(VT_TYPE);
}
template<typename T> const T *type_as() const;
const Null *type_as_Null() const {
return type_type() == Type_Null ? static_cast<const Null *>(type()) : nullptr;
}
const Int *type_as_Int() const {
return type_type() == Type_Int ? static_cast<const Int *>(type()) : nullptr;
}
const FloatingPoint *type_as_FloatingPoint() const {
return type_type() == Type_FloatingPoint ? static_cast<const FloatingPoint *>(type()) : nullptr;
}
const Binary *type_as_Binary() const {
return type_type() == Type_Binary ? static_cast<const Binary *>(type()) : nullptr;
}
const Utf8 *type_as_Utf8() const {
return type_type() == Type_Utf8 ? static_cast<const Utf8 *>(type()) : nullptr;
}
const Bool *type_as_Bool() const {
return type_type() == Type_Bool ? static_cast<const Bool *>(type()) : nullptr;
}
const Decimal *type_as_Decimal() const {
return type_type() == Type_Decimal ? static_cast<const Decimal *>(type()) : nullptr;
}
const Date *type_as_Date() const {
return type_type() == Type_Date ? static_cast<const Date *>(type()) : nullptr;
}
const Time *type_as_Time() const {
return type_type() == Type_Time ? static_cast<const Time *>(type()) : nullptr;
}
const Timestamp *type_as_Timestamp() const {
return type_type() == Type_Timestamp ? static_cast<const Timestamp *>(type()) : nullptr;
}
const Interval *type_as_Interval() const {
return type_type() == Type_Interval ? static_cast<const Interval *>(type()) : nullptr;
}
const List *type_as_List() const {
return type_type() == Type_List ? static_cast<const List *>(type()) : nullptr;
}
const Struct_ *type_as_Struct_() const {
return type_type() == Type_Struct_ ? static_cast<const Struct_ *>(type()) : nullptr;
}
const Union *type_as_Union() const {
return type_type() == Type_Union ? static_cast<const Union *>(type()) : nullptr;
}
const FixedSizeBinary *type_as_FixedSizeBinary() const {
return type_type() == Type_FixedSizeBinary ? static_cast<const FixedSizeBinary *>(type()) : nullptr;
}
const FixedSizeList *type_as_FixedSizeList() const {
return type_type() == Type_FixedSizeList ? static_cast<const FixedSizeList *>(type()) : nullptr;
}
const Map *type_as_Map() const {
return type_type() == Type_Map ? static_cast<const Map *>(type()) : nullptr;
}
const Duration *type_as_Duration() const {
return type_type() == Type_Duration ? static_cast<const Duration *>(type()) : nullptr;
}
const LargeBinary *type_as_LargeBinary() const {
return type_type() == Type_LargeBinary ? static_cast<const LargeBinary *>(type()) : nullptr;
}
const LargeUtf8 *type_as_LargeUtf8() const {
return type_type() == Type_LargeUtf8 ? static_cast<const LargeUtf8 *>(type()) : nullptr;
}
const LargeList *type_as_LargeList() const {
return type_type() == Type_LargeList ? static_cast<const LargeList *>(type()) : nullptr;
}
/// The dimensions of the tensor, optionally named.
const flatbuffers::Vector<flatbuffers::Offset<TensorDim>> *shape() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<TensorDim>> *>(VT_SHAPE);
}
/// The number of non-zero values in a sparse tensor.
int64_t non_zero_length() const {
return GetField<int64_t>(VT_NON_ZERO_LENGTH, 0);
}
SparseTensorIndex sparseIndex_type() const {
return static_cast<SparseTensorIndex>(GetField<uint8_t>(VT_SPARSEINDEX_TYPE, 0));
}
/// Sparse tensor index
const void *sparseIndex() const {
return GetPointer<const void *>(VT_SPARSEINDEX);
}
template<typename T> const T *sparseIndex_as() const;
const SparseTensorIndexCOO *sparseIndex_as_SparseTensorIndexCOO() const {
return sparseIndex_type() == SparseTensorIndex_SparseTensorIndexCOO ? static_cast<const SparseTensorIndexCOO *>(sparseIndex()) : nullptr;
}
const SparseMatrixIndexCSR *sparseIndex_as_SparseMatrixIndexCSR() const {
return sparseIndex_type() == SparseTensorIndex_SparseMatrixIndexCSR ? static_cast<const SparseMatrixIndexCSR *>(sparseIndex()) : nullptr;
}
/// The location and size of the tensor's data
const Buffer *data() const {
return GetStruct<const Buffer *>(VT_DATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_TYPE_TYPE) &&
VerifyOffset(verifier, VT_TYPE) &&
VerifyType(verifier, type(), type_type()) &&
VerifyOffset(verifier, VT_SHAPE) &&
verifier.VerifyVector(shape()) &&
verifier.VerifyVectorOfTables(shape()) &&
VerifyField<int64_t>(verifier, VT_NON_ZERO_LENGTH) &&
VerifyField<uint8_t>(verifier, VT_SPARSEINDEX_TYPE) &&
VerifyOffset(verifier, VT_SPARSEINDEX) &&
VerifySparseTensorIndex(verifier, sparseIndex(), sparseIndex_type()) &&
VerifyField<Buffer>(verifier, VT_DATA) &&
verifier.EndTable();
}
};
template<> inline const Null *SparseTensor::type_as<Null>() const {
return type_as_Null();
}
template<> inline const Int *SparseTensor::type_as<Int>() const {
return type_as_Int();
}
template<> inline const FloatingPoint *SparseTensor::type_as<FloatingPoint>() const {
return type_as_FloatingPoint();
}
template<> inline const Binary *SparseTensor::type_as<Binary>() const {
return type_as_Binary();
}
template<> inline const Utf8 *SparseTensor::type_as<Utf8>() const {
return type_as_Utf8();
}
template<> inline const Bool *SparseTensor::type_as<Bool>() const {
return type_as_Bool();
}
template<> inline const Decimal *SparseTensor::type_as<Decimal>() const {
return type_as_Decimal();
}
template<> inline const Date *SparseTensor::type_as<Date>() const {
return type_as_Date();
}
template<> inline const Time *SparseTensor::type_as<Time>() const {
return type_as_Time();
}
template<> inline const Timestamp *SparseTensor::type_as<Timestamp>() const {
return type_as_Timestamp();
}
template<> inline const Interval *SparseTensor::type_as<Interval>() const {
return type_as_Interval();
}
template<> inline const List *SparseTensor::type_as<List>() const {
return type_as_List();
}
template<> inline const Struct_ *SparseTensor::type_as<Struct_>() const {
return type_as_Struct_();
}
template<> inline const Union *SparseTensor::type_as<Union>() const {
return type_as_Union();
}
template<> inline const FixedSizeBinary *SparseTensor::type_as<FixedSizeBinary>() const {
return type_as_FixedSizeBinary();
}
template<> inline const FixedSizeList *SparseTensor::type_as<FixedSizeList>() const {
return type_as_FixedSizeList();
}
template<> inline const Map *SparseTensor::type_as<Map>() const {
return type_as_Map();
}
template<> inline const Duration *SparseTensor::type_as<Duration>() const {
return type_as_Duration();
}
template<> inline const LargeBinary *SparseTensor::type_as<LargeBinary>() const {
return type_as_LargeBinary();
}
template<> inline const LargeUtf8 *SparseTensor::type_as<LargeUtf8>() const {
return type_as_LargeUtf8();
}
template<> inline const LargeList *SparseTensor::type_as<LargeList>() const {
return type_as_LargeList();
}
template<> inline const SparseTensorIndexCOO *SparseTensor::sparseIndex_as<SparseTensorIndexCOO>() const {
return sparseIndex_as_SparseTensorIndexCOO();
}
template<> inline const SparseMatrixIndexCSR *SparseTensor::sparseIndex_as<SparseMatrixIndexCSR>() const {
return sparseIndex_as_SparseMatrixIndexCSR();
}
struct SparseTensorBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_type_type(Type type_type) {
fbb_.AddElement<uint8_t>(SparseTensor::VT_TYPE_TYPE, static_cast<uint8_t>(type_type), 0);
}
void add_type(flatbuffers::Offset<void> type) {
fbb_.AddOffset(SparseTensor::VT_TYPE, type);
}
void add_shape(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TensorDim>>> shape) {
fbb_.AddOffset(SparseTensor::VT_SHAPE, shape);
}
void add_non_zero_length(int64_t non_zero_length) {
fbb_.AddElement<int64_t>(SparseTensor::VT_NON_ZERO_LENGTH, non_zero_length, 0);
}
void add_sparseIndex_type(SparseTensorIndex sparseIndex_type) {
fbb_.AddElement<uint8_t>(SparseTensor::VT_SPARSEINDEX_TYPE, static_cast<uint8_t>(sparseIndex_type), 0);
}
void add_sparseIndex(flatbuffers::Offset<void> sparseIndex) {
fbb_.AddOffset(SparseTensor::VT_SPARSEINDEX, sparseIndex);
}
void add_data(const Buffer *data) {
fbb_.AddStruct(SparseTensor::VT_DATA, data);
}
explicit SparseTensorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
SparseTensorBuilder &operator=(const SparseTensorBuilder &);
flatbuffers::Offset<SparseTensor> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<SparseTensor>(end);
return o;
}
};
inline flatbuffers::Offset<SparseTensor> CreateSparseTensor(
flatbuffers::FlatBufferBuilder &_fbb,
Type type_type = Type_NONE,
flatbuffers::Offset<void> type = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TensorDim>>> shape = 0,
int64_t non_zero_length = 0,
SparseTensorIndex sparseIndex_type = SparseTensorIndex_NONE,
flatbuffers::Offset<void> sparseIndex = 0,
const Buffer *data = 0) {
SparseTensorBuilder builder_(_fbb);
builder_.add_non_zero_length(non_zero_length);
builder_.add_data(data);
builder_.add_sparseIndex(sparseIndex);
builder_.add_shape(shape);
builder_.add_type(type);
builder_.add_sparseIndex_type(sparseIndex_type);
builder_.add_type_type(type_type);
return builder_.Finish();
}
inline flatbuffers::Offset<SparseTensor> CreateSparseTensorDirect(
flatbuffers::FlatBufferBuilder &_fbb,
Type type_type = Type_NONE,
flatbuffers::Offset<void> type = 0,
const std::vector<flatbuffers::Offset<TensorDim>> *shape = nullptr,
int64_t non_zero_length = 0,
SparseTensorIndex sparseIndex_type = SparseTensorIndex_NONE,
flatbuffers::Offset<void> sparseIndex = 0,
const Buffer *data = 0) {
auto shape__ = shape ? _fbb.CreateVector<flatbuffers::Offset<TensorDim>>(*shape) : 0;
return org::apache::arrow::flatbuf::CreateSparseTensor(
_fbb,
type_type,
type,
shape__,
non_zero_length,
sparseIndex_type,
sparseIndex,
data);
}
inline bool VerifySparseTensorIndex(flatbuffers::Verifier &verifier, const void *obj, SparseTensorIndex type) {
switch (type) {
case SparseTensorIndex_NONE: {
return true;
}
case SparseTensorIndex_SparseTensorIndexCOO: {
auto ptr = reinterpret_cast<const SparseTensorIndexCOO *>(obj);
return verifier.VerifyTable(ptr);
}
case SparseTensorIndex_SparseMatrixIndexCSR: {
auto ptr = reinterpret_cast<const SparseMatrixIndexCSR *>(obj);
return verifier.VerifyTable(ptr);
}
default: return false;
}
}
inline bool VerifySparseTensorIndexVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifySparseTensorIndex(
verifier, values->Get(i), types->GetEnum<SparseTensorIndex>(i))) {
return false;
}
}
return true;
}
inline const org::apache::arrow::flatbuf::SparseTensor *GetSparseTensor(const void *buf) {
return flatbuffers::GetRoot<org::apache::arrow::flatbuf::SparseTensor>(buf);
}
inline const org::apache::arrow::flatbuf::SparseTensor *GetSizePrefixedSparseTensor(const void *buf) {
return flatbuffers::GetSizePrefixedRoot<org::apache::arrow::flatbuf::SparseTensor>(buf);
}
inline bool VerifySparseTensorBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<org::apache::arrow::flatbuf::SparseTensor>(nullptr);
}
inline bool VerifySizePrefixedSparseTensorBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<org::apache::arrow::flatbuf::SparseTensor>(nullptr);
}
inline void FinishSparseTensorBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::SparseTensor> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedSparseTensorBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::SparseTensor> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace flatbuf
} // namespace arrow
} // namespace apache
} // namespace org
#endif // FLATBUFFERS_GENERATED_SPARSETENSOR_ORG_APACHE_ARROW_FLATBUF_H_

View File

@ -1,377 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_TENSOR_ORG_APACHE_ARROW_FLATBUF_H_
#define FLATBUFFERS_GENERATED_TENSOR_ORG_APACHE_ARROW_FLATBUF_H_
#include "flatbuffers/flatbuffers.h"
#include "Schema_generated.h"
namespace org {
namespace apache {
namespace arrow {
namespace flatbuf {
struct TensorDim;
struct Tensor;
/// ----------------------------------------------------------------------
/// Data structures for dense tensors
/// Shape data for a single axis in a tensor
struct TensorDim FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_SIZE = 4,
VT_NAME = 6
};
/// Length of dimension
int64_t size() const {
return GetField<int64_t>(VT_SIZE, 0);
}
/// Name of the dimension, optional
const flatbuffers::String *name() const {
return GetPointer<const flatbuffers::String *>(VT_NAME);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int64_t>(verifier, VT_SIZE) &&
VerifyOffset(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
verifier.EndTable();
}
};
struct TensorDimBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_size(int64_t size) {
fbb_.AddElement<int64_t>(TensorDim::VT_SIZE, size, 0);
}
void add_name(flatbuffers::Offset<flatbuffers::String> name) {
fbb_.AddOffset(TensorDim::VT_NAME, name);
}
explicit TensorDimBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
TensorDimBuilder &operator=(const TensorDimBuilder &);
flatbuffers::Offset<TensorDim> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<TensorDim>(end);
return o;
}
};
inline flatbuffers::Offset<TensorDim> CreateTensorDim(
flatbuffers::FlatBufferBuilder &_fbb,
int64_t size = 0,
flatbuffers::Offset<flatbuffers::String> name = 0) {
TensorDimBuilder builder_(_fbb);
builder_.add_size(size);
builder_.add_name(name);
return builder_.Finish();
}
inline flatbuffers::Offset<TensorDim> CreateTensorDimDirect(
flatbuffers::FlatBufferBuilder &_fbb,
int64_t size = 0,
const char *name = nullptr) {
auto name__ = name ? _fbb.CreateString(name) : 0;
return org::apache::arrow::flatbuf::CreateTensorDim(
_fbb,
size,
name__);
}
struct Tensor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_TYPE_TYPE = 4,
VT_TYPE = 6,
VT_SHAPE = 8,
VT_STRIDES = 10,
VT_DATA = 12
};
Type type_type() const {
return static_cast<Type>(GetField<uint8_t>(VT_TYPE_TYPE, 0));
}
/// The type of data contained in a value cell. Currently only fixed-width
/// value types are supported, no strings or nested types
const void *type() const {
return GetPointer<const void *>(VT_TYPE);
}
template<typename T> const T *type_as() const;
const Null *type_as_Null() const {
return type_type() == Type_Null ? static_cast<const Null *>(type()) : nullptr;
}
const Int *type_as_Int() const {
return type_type() == Type_Int ? static_cast<const Int *>(type()) : nullptr;
}
const FloatingPoint *type_as_FloatingPoint() const {
return type_type() == Type_FloatingPoint ? static_cast<const FloatingPoint *>(type()) : nullptr;
}
const Binary *type_as_Binary() const {
return type_type() == Type_Binary ? static_cast<const Binary *>(type()) : nullptr;
}
const Utf8 *type_as_Utf8() const {
return type_type() == Type_Utf8 ? static_cast<const Utf8 *>(type()) : nullptr;
}
const Bool *type_as_Bool() const {
return type_type() == Type_Bool ? static_cast<const Bool *>(type()) : nullptr;
}
const Decimal *type_as_Decimal() const {
return type_type() == Type_Decimal ? static_cast<const Decimal *>(type()) : nullptr;
}
const Date *type_as_Date() const {
return type_type() == Type_Date ? static_cast<const Date *>(type()) : nullptr;
}
const Time *type_as_Time() const {
return type_type() == Type_Time ? static_cast<const Time *>(type()) : nullptr;
}
const Timestamp *type_as_Timestamp() const {
return type_type() == Type_Timestamp ? static_cast<const Timestamp *>(type()) : nullptr;
}
const Interval *type_as_Interval() const {
return type_type() == Type_Interval ? static_cast<const Interval *>(type()) : nullptr;
}
const List *type_as_List() const {
return type_type() == Type_List ? static_cast<const List *>(type()) : nullptr;
}
const Struct_ *type_as_Struct_() const {
return type_type() == Type_Struct_ ? static_cast<const Struct_ *>(type()) : nullptr;
}
const Union *type_as_Union() const {
return type_type() == Type_Union ? static_cast<const Union *>(type()) : nullptr;
}
const FixedSizeBinary *type_as_FixedSizeBinary() const {
return type_type() == Type_FixedSizeBinary ? static_cast<const FixedSizeBinary *>(type()) : nullptr;
}
const FixedSizeList *type_as_FixedSizeList() const {
return type_type() == Type_FixedSizeList ? static_cast<const FixedSizeList *>(type()) : nullptr;
}
const Map *type_as_Map() const {
return type_type() == Type_Map ? static_cast<const Map *>(type()) : nullptr;
}
const Duration *type_as_Duration() const {
return type_type() == Type_Duration ? static_cast<const Duration *>(type()) : nullptr;
}
const LargeBinary *type_as_LargeBinary() const {
return type_type() == Type_LargeBinary ? static_cast<const LargeBinary *>(type()) : nullptr;
}
const LargeUtf8 *type_as_LargeUtf8() const {
return type_type() == Type_LargeUtf8 ? static_cast<const LargeUtf8 *>(type()) : nullptr;
}
const LargeList *type_as_LargeList() const {
return type_type() == Type_LargeList ? static_cast<const LargeList *>(type()) : nullptr;
}
/// The dimensions of the tensor, optionally named
const flatbuffers::Vector<flatbuffers::Offset<TensorDim>> *shape() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<TensorDim>> *>(VT_SHAPE);
}
/// Non-negative byte offsets to advance one value cell along each dimension
const flatbuffers::Vector<int64_t> *strides() const {
return GetPointer<const flatbuffers::Vector<int64_t> *>(VT_STRIDES);
}
/// The location and size of the tensor's data
const Buffer *data() const {
return GetStruct<const Buffer *>(VT_DATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_TYPE_TYPE) &&
VerifyOffset(verifier, VT_TYPE) &&
VerifyType(verifier, type(), type_type()) &&
VerifyOffset(verifier, VT_SHAPE) &&
verifier.VerifyVector(shape()) &&
verifier.VerifyVectorOfTables(shape()) &&
VerifyOffset(verifier, VT_STRIDES) &&
verifier.VerifyVector(strides()) &&
VerifyField<Buffer>(verifier, VT_DATA) &&
verifier.EndTable();
}
};
template<> inline const Null *Tensor::type_as<Null>() const {
return type_as_Null();
}
template<> inline const Int *Tensor::type_as<Int>() const {
return type_as_Int();
}
template<> inline const FloatingPoint *Tensor::type_as<FloatingPoint>() const {
return type_as_FloatingPoint();
}
template<> inline const Binary *Tensor::type_as<Binary>() const {
return type_as_Binary();
}
template<> inline const Utf8 *Tensor::type_as<Utf8>() const {
return type_as_Utf8();
}
template<> inline const Bool *Tensor::type_as<Bool>() const {
return type_as_Bool();
}
template<> inline const Decimal *Tensor::type_as<Decimal>() const {
return type_as_Decimal();
}
template<> inline const Date *Tensor::type_as<Date>() const {
return type_as_Date();
}
template<> inline const Time *Tensor::type_as<Time>() const {
return type_as_Time();
}
template<> inline const Timestamp *Tensor::type_as<Timestamp>() const {
return type_as_Timestamp();
}
template<> inline const Interval *Tensor::type_as<Interval>() const {
return type_as_Interval();
}
template<> inline const List *Tensor::type_as<List>() const {
return type_as_List();
}
template<> inline const Struct_ *Tensor::type_as<Struct_>() const {
return type_as_Struct_();
}
template<> inline const Union *Tensor::type_as<Union>() const {
return type_as_Union();
}
template<> inline const FixedSizeBinary *Tensor::type_as<FixedSizeBinary>() const {
return type_as_FixedSizeBinary();
}
template<> inline const FixedSizeList *Tensor::type_as<FixedSizeList>() const {
return type_as_FixedSizeList();
}
template<> inline const Map *Tensor::type_as<Map>() const {
return type_as_Map();
}
template<> inline const Duration *Tensor::type_as<Duration>() const {
return type_as_Duration();
}
template<> inline const LargeBinary *Tensor::type_as<LargeBinary>() const {
return type_as_LargeBinary();
}
template<> inline const LargeUtf8 *Tensor::type_as<LargeUtf8>() const {
return type_as_LargeUtf8();
}
template<> inline const LargeList *Tensor::type_as<LargeList>() const {
return type_as_LargeList();
}
struct TensorBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_type_type(Type type_type) {
fbb_.AddElement<uint8_t>(Tensor::VT_TYPE_TYPE, static_cast<uint8_t>(type_type), 0);
}
void add_type(flatbuffers::Offset<void> type) {
fbb_.AddOffset(Tensor::VT_TYPE, type);
}
void add_shape(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TensorDim>>> shape) {
fbb_.AddOffset(Tensor::VT_SHAPE, shape);
}
void add_strides(flatbuffers::Offset<flatbuffers::Vector<int64_t>> strides) {
fbb_.AddOffset(Tensor::VT_STRIDES, strides);
}
void add_data(const Buffer *data) {
fbb_.AddStruct(Tensor::VT_DATA, data);
}
explicit TensorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
TensorBuilder &operator=(const TensorBuilder &);
flatbuffers::Offset<Tensor> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Tensor>(end);
return o;
}
};
inline flatbuffers::Offset<Tensor> CreateTensor(
flatbuffers::FlatBufferBuilder &_fbb,
Type type_type = Type_NONE,
flatbuffers::Offset<void> type = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TensorDim>>> shape = 0,
flatbuffers::Offset<flatbuffers::Vector<int64_t>> strides = 0,
const Buffer *data = 0) {
TensorBuilder builder_(_fbb);
builder_.add_data(data);
builder_.add_strides(strides);
builder_.add_shape(shape);
builder_.add_type(type);
builder_.add_type_type(type_type);
return builder_.Finish();
}
inline flatbuffers::Offset<Tensor> CreateTensorDirect(
flatbuffers::FlatBufferBuilder &_fbb,
Type type_type = Type_NONE,
flatbuffers::Offset<void> type = 0,
const std::vector<flatbuffers::Offset<TensorDim>> *shape = nullptr,
const std::vector<int64_t> *strides = nullptr,
const Buffer *data = 0) {
auto shape__ = shape ? _fbb.CreateVector<flatbuffers::Offset<TensorDim>>(*shape) : 0;
auto strides__ = strides ? _fbb.CreateVector<int64_t>(*strides) : 0;
return org::apache::arrow::flatbuf::CreateTensor(
_fbb,
type_type,
type,
shape__,
strides__,
data);
}
inline const org::apache::arrow::flatbuf::Tensor *GetTensor(const void *buf) {
return flatbuffers::GetRoot<org::apache::arrow::flatbuf::Tensor>(buf);
}
inline const org::apache::arrow::flatbuf::Tensor *GetSizePrefixedTensor(const void *buf) {
return flatbuffers::GetSizePrefixedRoot<org::apache::arrow::flatbuf::Tensor>(buf);
}
inline bool VerifyTensorBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<org::apache::arrow::flatbuf::Tensor>(nullptr);
}
inline bool VerifySizePrefixedTensorBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<org::apache::arrow::flatbuf::Tensor>(nullptr);
}
inline void FinishTensorBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Tensor> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedTensorBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<org::apache::arrow::flatbuf::Tensor> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace flatbuf
} // namespace arrow
} // namespace apache
} // namespace org
#endif // FLATBUFFERS_GENERATED_TENSOR_ORG_APACHE_ARROW_FLATBUF_H_

View File

@ -1,839 +0,0 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_FEATHER_ARROW_IPC_FEATHER_FBS_H_
#define FLATBUFFERS_GENERATED_FEATHER_ARROW_IPC_FEATHER_FBS_H_
#include "flatbuffers/flatbuffers.h"
namespace arrow {
namespace ipc {
namespace feather {
namespace fbs {
struct PrimitiveArray;
struct CategoryMetadata;
struct TimestampMetadata;
struct DateMetadata;
struct TimeMetadata;
struct Column;
struct CTable;
/// Feather is an experimental serialization format implemented using
/// techniques from Apache Arrow. It was created as a proof-of-concept of an
/// interoperable file format for storing data frames originating in Python or
/// R. It enabled the developers to sidestep some of the open design questions
/// in Arrow from early 2016 and instead create something simple and useful for
/// the intended use cases.
enum Type {
Type_BOOL = 0,
Type_INT8 = 1,
Type_INT16 = 2,
Type_INT32 = 3,
Type_INT64 = 4,
Type_UINT8 = 5,
Type_UINT16 = 6,
Type_UINT32 = 7,
Type_UINT64 = 8,
Type_FLOAT = 9,
Type_DOUBLE = 10,
Type_UTF8 = 11,
Type_BINARY = 12,
Type_CATEGORY = 13,
Type_TIMESTAMP = 14,
Type_DATE = 15,
Type_TIME = 16,
Type_LARGE_UTF8 = 17,
Type_LARGE_BINARY = 18,
Type_MIN = Type_BOOL,
Type_MAX = Type_LARGE_BINARY
};
inline const Type (&EnumValuesType())[19] {
static const Type values[] = {
Type_BOOL,
Type_INT8,
Type_INT16,
Type_INT32,
Type_INT64,
Type_UINT8,
Type_UINT16,
Type_UINT32,
Type_UINT64,
Type_FLOAT,
Type_DOUBLE,
Type_UTF8,
Type_BINARY,
Type_CATEGORY,
Type_TIMESTAMP,
Type_DATE,
Type_TIME,
Type_LARGE_UTF8,
Type_LARGE_BINARY
};
return values;
}
inline const char * const *EnumNamesType() {
static const char * const names[] = {
"BOOL",
"INT8",
"INT16",
"INT32",
"INT64",
"UINT8",
"UINT16",
"UINT32",
"UINT64",
"FLOAT",
"DOUBLE",
"UTF8",
"BINARY",
"CATEGORY",
"TIMESTAMP",
"DATE",
"TIME",
"LARGE_UTF8",
"LARGE_BINARY",
nullptr
};
return names;
}
inline const char *EnumNameType(Type e) {
if (e < Type_BOOL || e > Type_LARGE_BINARY) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesType()[index];
}
enum Encoding {
Encoding_PLAIN = 0 /// Data is stored dictionary-encoded
/// dictionary size: <INT32 Dictionary size>
/// dictionary data: <TYPE primitive array>
/// dictionary index: <INT32 primitive array>
///
/// TODO: do we care about storing the index values in a smaller typeclass
,
Encoding_DICTIONARY = 1,
Encoding_MIN = Encoding_PLAIN,
Encoding_MAX = Encoding_DICTIONARY
};
inline const Encoding (&EnumValuesEncoding())[2] {
static const Encoding values[] = {
Encoding_PLAIN,
Encoding_DICTIONARY
};
return values;
}
inline const char * const *EnumNamesEncoding() {
static const char * const names[] = {
"PLAIN",
"DICTIONARY",
nullptr
};
return names;
}
inline const char *EnumNameEncoding(Encoding e) {
if (e < Encoding_PLAIN || e > Encoding_DICTIONARY) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesEncoding()[index];
}
enum TimeUnit {
TimeUnit_SECOND = 0,
TimeUnit_MILLISECOND = 1,
TimeUnit_MICROSECOND = 2,
TimeUnit_NANOSECOND = 3,
TimeUnit_MIN = TimeUnit_SECOND,
TimeUnit_MAX = TimeUnit_NANOSECOND
};
inline const TimeUnit (&EnumValuesTimeUnit())[4] {
static const TimeUnit values[] = {
TimeUnit_SECOND,
TimeUnit_MILLISECOND,
TimeUnit_MICROSECOND,
TimeUnit_NANOSECOND
};
return values;
}
inline const char * const *EnumNamesTimeUnit() {
static const char * const names[] = {
"SECOND",
"MILLISECOND",
"MICROSECOND",
"NANOSECOND",
nullptr
};
return names;
}
inline const char *EnumNameTimeUnit(TimeUnit e) {
if (e < TimeUnit_SECOND || e > TimeUnit_NANOSECOND) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesTimeUnit()[index];
}
enum TypeMetadata {
TypeMetadata_NONE = 0,
TypeMetadata_CategoryMetadata = 1,
TypeMetadata_TimestampMetadata = 2,
TypeMetadata_DateMetadata = 3,
TypeMetadata_TimeMetadata = 4,
TypeMetadata_MIN = TypeMetadata_NONE,
TypeMetadata_MAX = TypeMetadata_TimeMetadata
};
inline const TypeMetadata (&EnumValuesTypeMetadata())[5] {
static const TypeMetadata values[] = {
TypeMetadata_NONE,
TypeMetadata_CategoryMetadata,
TypeMetadata_TimestampMetadata,
TypeMetadata_DateMetadata,
TypeMetadata_TimeMetadata
};
return values;
}
inline const char * const *EnumNamesTypeMetadata() {
static const char * const names[] = {
"NONE",
"CategoryMetadata",
"TimestampMetadata",
"DateMetadata",
"TimeMetadata",
nullptr
};
return names;
}
inline const char *EnumNameTypeMetadata(TypeMetadata e) {
if (e < TypeMetadata_NONE || e > TypeMetadata_TimeMetadata) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesTypeMetadata()[index];
}
template<typename T> struct TypeMetadataTraits {
static const TypeMetadata enum_value = TypeMetadata_NONE;
};
template<> struct TypeMetadataTraits<CategoryMetadata> {
static const TypeMetadata enum_value = TypeMetadata_CategoryMetadata;
};
template<> struct TypeMetadataTraits<TimestampMetadata> {
static const TypeMetadata enum_value = TypeMetadata_TimestampMetadata;
};
template<> struct TypeMetadataTraits<DateMetadata> {
static const TypeMetadata enum_value = TypeMetadata_DateMetadata;
};
template<> struct TypeMetadataTraits<TimeMetadata> {
static const TypeMetadata enum_value = TypeMetadata_TimeMetadata;
};
bool VerifyTypeMetadata(flatbuffers::Verifier &verifier, const void *obj, TypeMetadata type);
bool VerifyTypeMetadataVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
struct PrimitiveArray FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_TYPE = 4,
VT_ENCODING = 6,
VT_OFFSET = 8,
VT_LENGTH = 10,
VT_NULL_COUNT = 12,
VT_TOTAL_BYTES = 14
};
Type type() const {
return static_cast<Type>(GetField<int8_t>(VT_TYPE, 0));
}
Encoding encoding() const {
return static_cast<Encoding>(GetField<int8_t>(VT_ENCODING, 0));
}
/// Relative memory offset of the start of the array data excluding the size
/// of the metadata
int64_t offset() const {
return GetField<int64_t>(VT_OFFSET, 0);
}
/// The number of logical values in the array
int64_t length() const {
return GetField<int64_t>(VT_LENGTH, 0);
}
/// The number of observed nulls
int64_t null_count() const {
return GetField<int64_t>(VT_NULL_COUNT, 0);
}
/// The total size of the actual data in the file
int64_t total_bytes() const {
return GetField<int64_t>(VT_TOTAL_BYTES, 0);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_TYPE) &&
VerifyField<int8_t>(verifier, VT_ENCODING) &&
VerifyField<int64_t>(verifier, VT_OFFSET) &&
VerifyField<int64_t>(verifier, VT_LENGTH) &&
VerifyField<int64_t>(verifier, VT_NULL_COUNT) &&
VerifyField<int64_t>(verifier, VT_TOTAL_BYTES) &&
verifier.EndTable();
}
};
struct PrimitiveArrayBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_type(Type type) {
fbb_.AddElement<int8_t>(PrimitiveArray::VT_TYPE, static_cast<int8_t>(type), 0);
}
void add_encoding(Encoding encoding) {
fbb_.AddElement<int8_t>(PrimitiveArray::VT_ENCODING, static_cast<int8_t>(encoding), 0);
}
void add_offset(int64_t offset) {
fbb_.AddElement<int64_t>(PrimitiveArray::VT_OFFSET, offset, 0);
}
void add_length(int64_t length) {
fbb_.AddElement<int64_t>(PrimitiveArray::VT_LENGTH, length, 0);
}
void add_null_count(int64_t null_count) {
fbb_.AddElement<int64_t>(PrimitiveArray::VT_NULL_COUNT, null_count, 0);
}
void add_total_bytes(int64_t total_bytes) {
fbb_.AddElement<int64_t>(PrimitiveArray::VT_TOTAL_BYTES, total_bytes, 0);
}
explicit PrimitiveArrayBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
PrimitiveArrayBuilder &operator=(const PrimitiveArrayBuilder &);
flatbuffers::Offset<PrimitiveArray> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<PrimitiveArray>(end);
return o;
}
};
inline flatbuffers::Offset<PrimitiveArray> CreatePrimitiveArray(
flatbuffers::FlatBufferBuilder &_fbb,
Type type = Type_BOOL,
Encoding encoding = Encoding_PLAIN,
int64_t offset = 0,
int64_t length = 0,
int64_t null_count = 0,
int64_t total_bytes = 0) {
PrimitiveArrayBuilder builder_(_fbb);
builder_.add_total_bytes(total_bytes);
builder_.add_null_count(null_count);
builder_.add_length(length);
builder_.add_offset(offset);
builder_.add_encoding(encoding);
builder_.add_type(type);
return builder_.Finish();
}
struct CategoryMetadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_LEVELS = 4,
VT_ORDERED = 6
};
/// The category codes are presumed to be integers that are valid indexes into
/// the levels array
const PrimitiveArray *levels() const {
return GetPointer<const PrimitiveArray *>(VT_LEVELS);
}
bool ordered() const {
return GetField<uint8_t>(VT_ORDERED, 0) != 0;
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_LEVELS) &&
verifier.VerifyTable(levels()) &&
VerifyField<uint8_t>(verifier, VT_ORDERED) &&
verifier.EndTable();
}
};
struct CategoryMetadataBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_levels(flatbuffers::Offset<PrimitiveArray> levels) {
fbb_.AddOffset(CategoryMetadata::VT_LEVELS, levels);
}
void add_ordered(bool ordered) {
fbb_.AddElement<uint8_t>(CategoryMetadata::VT_ORDERED, static_cast<uint8_t>(ordered), 0);
}
explicit CategoryMetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
CategoryMetadataBuilder &operator=(const CategoryMetadataBuilder &);
flatbuffers::Offset<CategoryMetadata> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<CategoryMetadata>(end);
return o;
}
};
inline flatbuffers::Offset<CategoryMetadata> CreateCategoryMetadata(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<PrimitiveArray> levels = 0,
bool ordered = false) {
CategoryMetadataBuilder builder_(_fbb);
builder_.add_levels(levels);
builder_.add_ordered(ordered);
return builder_.Finish();
}
struct TimestampMetadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_UNIT = 4,
VT_TIMEZONE = 6
};
TimeUnit unit() const {
return static_cast<TimeUnit>(GetField<int8_t>(VT_UNIT, 0));
}
/// Timestamp data is assumed to be UTC, but the time zone is stored here for
/// presentation as localized
const flatbuffers::String *timezone() const {
return GetPointer<const flatbuffers::String *>(VT_TIMEZONE);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_UNIT) &&
VerifyOffset(verifier, VT_TIMEZONE) &&
verifier.VerifyString(timezone()) &&
verifier.EndTable();
}
};
struct TimestampMetadataBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_unit(TimeUnit unit) {
fbb_.AddElement<int8_t>(TimestampMetadata::VT_UNIT, static_cast<int8_t>(unit), 0);
}
void add_timezone(flatbuffers::Offset<flatbuffers::String> timezone) {
fbb_.AddOffset(TimestampMetadata::VT_TIMEZONE, timezone);
}
explicit TimestampMetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
TimestampMetadataBuilder &operator=(const TimestampMetadataBuilder &);
flatbuffers::Offset<TimestampMetadata> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<TimestampMetadata>(end);
return o;
}
};
inline flatbuffers::Offset<TimestampMetadata> CreateTimestampMetadata(
flatbuffers::FlatBufferBuilder &_fbb,
TimeUnit unit = TimeUnit_SECOND,
flatbuffers::Offset<flatbuffers::String> timezone = 0) {
TimestampMetadataBuilder builder_(_fbb);
builder_.add_timezone(timezone);
builder_.add_unit(unit);
return builder_.Finish();
}
inline flatbuffers::Offset<TimestampMetadata> CreateTimestampMetadataDirect(
flatbuffers::FlatBufferBuilder &_fbb,
TimeUnit unit = TimeUnit_SECOND,
const char *timezone = nullptr) {
auto timezone__ = timezone ? _fbb.CreateString(timezone) : 0;
return arrow::ipc::feather::fbs::CreateTimestampMetadata(
_fbb,
unit,
timezone__);
}
struct DateMetadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
verifier.EndTable();
}
};
struct DateMetadataBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
explicit DateMetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
DateMetadataBuilder &operator=(const DateMetadataBuilder &);
flatbuffers::Offset<DateMetadata> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<DateMetadata>(end);
return o;
}
};
inline flatbuffers::Offset<DateMetadata> CreateDateMetadata(
flatbuffers::FlatBufferBuilder &_fbb) {
DateMetadataBuilder builder_(_fbb);
return builder_.Finish();
}
struct TimeMetadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_UNIT = 4
};
TimeUnit unit() const {
return static_cast<TimeUnit>(GetField<int8_t>(VT_UNIT, 0));
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<int8_t>(verifier, VT_UNIT) &&
verifier.EndTable();
}
};
struct TimeMetadataBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_unit(TimeUnit unit) {
fbb_.AddElement<int8_t>(TimeMetadata::VT_UNIT, static_cast<int8_t>(unit), 0);
}
explicit TimeMetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
TimeMetadataBuilder &operator=(const TimeMetadataBuilder &);
flatbuffers::Offset<TimeMetadata> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<TimeMetadata>(end);
return o;
}
};
inline flatbuffers::Offset<TimeMetadata> CreateTimeMetadata(
flatbuffers::FlatBufferBuilder &_fbb,
TimeUnit unit = TimeUnit_SECOND) {
TimeMetadataBuilder builder_(_fbb);
builder_.add_unit(unit);
return builder_.Finish();
}
struct Column FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_NAME = 4,
VT_VALUES = 6,
VT_METADATA_TYPE = 8,
VT_METADATA = 10,
VT_USER_METADATA = 12
};
const flatbuffers::String *name() const {
return GetPointer<const flatbuffers::String *>(VT_NAME);
}
const PrimitiveArray *values() const {
return GetPointer<const PrimitiveArray *>(VT_VALUES);
}
TypeMetadata metadata_type() const {
return static_cast<TypeMetadata>(GetField<uint8_t>(VT_METADATA_TYPE, 0));
}
const void *metadata() const {
return GetPointer<const void *>(VT_METADATA);
}
template<typename T> const T *metadata_as() const;
const CategoryMetadata *metadata_as_CategoryMetadata() const {
return metadata_type() == TypeMetadata_CategoryMetadata ? static_cast<const CategoryMetadata *>(metadata()) : nullptr;
}
const TimestampMetadata *metadata_as_TimestampMetadata() const {
return metadata_type() == TypeMetadata_TimestampMetadata ? static_cast<const TimestampMetadata *>(metadata()) : nullptr;
}
const DateMetadata *metadata_as_DateMetadata() const {
return metadata_type() == TypeMetadata_DateMetadata ? static_cast<const DateMetadata *>(metadata()) : nullptr;
}
const TimeMetadata *metadata_as_TimeMetadata() const {
return metadata_type() == TypeMetadata_TimeMetadata ? static_cast<const TimeMetadata *>(metadata()) : nullptr;
}
/// This should (probably) be JSON
const flatbuffers::String *user_metadata() const {
return GetPointer<const flatbuffers::String *>(VT_USER_METADATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_NAME) &&
verifier.VerifyString(name()) &&
VerifyOffset(verifier, VT_VALUES) &&
verifier.VerifyTable(values()) &&
VerifyField<uint8_t>(verifier, VT_METADATA_TYPE) &&
VerifyOffset(verifier, VT_METADATA) &&
VerifyTypeMetadata(verifier, metadata(), metadata_type()) &&
VerifyOffset(verifier, VT_USER_METADATA) &&
verifier.VerifyString(user_metadata()) &&
verifier.EndTable();
}
};
template<> inline const CategoryMetadata *Column::metadata_as<CategoryMetadata>() const {
return metadata_as_CategoryMetadata();
}
template<> inline const TimestampMetadata *Column::metadata_as<TimestampMetadata>() const {
return metadata_as_TimestampMetadata();
}
template<> inline const DateMetadata *Column::metadata_as<DateMetadata>() const {
return metadata_as_DateMetadata();
}
template<> inline const TimeMetadata *Column::metadata_as<TimeMetadata>() const {
return metadata_as_TimeMetadata();
}
struct ColumnBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_name(flatbuffers::Offset<flatbuffers::String> name) {
fbb_.AddOffset(Column::VT_NAME, name);
}
void add_values(flatbuffers::Offset<PrimitiveArray> values) {
fbb_.AddOffset(Column::VT_VALUES, values);
}
void add_metadata_type(TypeMetadata metadata_type) {
fbb_.AddElement<uint8_t>(Column::VT_METADATA_TYPE, static_cast<uint8_t>(metadata_type), 0);
}
void add_metadata(flatbuffers::Offset<void> metadata) {
fbb_.AddOffset(Column::VT_METADATA, metadata);
}
void add_user_metadata(flatbuffers::Offset<flatbuffers::String> user_metadata) {
fbb_.AddOffset(Column::VT_USER_METADATA, user_metadata);
}
explicit ColumnBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
ColumnBuilder &operator=(const ColumnBuilder &);
flatbuffers::Offset<Column> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Column>(end);
return o;
}
};
inline flatbuffers::Offset<Column> CreateColumn(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> name = 0,
flatbuffers::Offset<PrimitiveArray> values = 0,
TypeMetadata metadata_type = TypeMetadata_NONE,
flatbuffers::Offset<void> metadata = 0,
flatbuffers::Offset<flatbuffers::String> user_metadata = 0) {
ColumnBuilder builder_(_fbb);
builder_.add_user_metadata(user_metadata);
builder_.add_metadata(metadata);
builder_.add_values(values);
builder_.add_name(name);
builder_.add_metadata_type(metadata_type);
return builder_.Finish();
}
inline flatbuffers::Offset<Column> CreateColumnDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *name = nullptr,
flatbuffers::Offset<PrimitiveArray> values = 0,
TypeMetadata metadata_type = TypeMetadata_NONE,
flatbuffers::Offset<void> metadata = 0,
const char *user_metadata = nullptr) {
auto name__ = name ? _fbb.CreateString(name) : 0;
auto user_metadata__ = user_metadata ? _fbb.CreateString(user_metadata) : 0;
return arrow::ipc::feather::fbs::CreateColumn(
_fbb,
name__,
values,
metadata_type,
metadata,
user_metadata__);
}
struct CTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_DESCRIPTION = 4,
VT_NUM_ROWS = 6,
VT_COLUMNS = 8,
VT_VERSION = 10,
VT_METADATA = 12
};
/// Some text (or a name) metadata about what the file is, optional
const flatbuffers::String *description() const {
return GetPointer<const flatbuffers::String *>(VT_DESCRIPTION);
}
int64_t num_rows() const {
return GetField<int64_t>(VT_NUM_ROWS, 0);
}
const flatbuffers::Vector<flatbuffers::Offset<Column>> *columns() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Column>> *>(VT_COLUMNS);
}
/// Version number of the Feather format
int32_t version() const {
return GetField<int32_t>(VT_VERSION, 0);
}
/// Table metadata (likely JSON), not yet used
const flatbuffers::String *metadata() const {
return GetPointer<const flatbuffers::String *>(VT_METADATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_DESCRIPTION) &&
verifier.VerifyString(description()) &&
VerifyField<int64_t>(verifier, VT_NUM_ROWS) &&
VerifyOffset(verifier, VT_COLUMNS) &&
verifier.VerifyVector(columns()) &&
verifier.VerifyVectorOfTables(columns()) &&
VerifyField<int32_t>(verifier, VT_VERSION) &&
VerifyOffset(verifier, VT_METADATA) &&
verifier.VerifyString(metadata()) &&
verifier.EndTable();
}
};
struct CTableBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_description(flatbuffers::Offset<flatbuffers::String> description) {
fbb_.AddOffset(CTable::VT_DESCRIPTION, description);
}
void add_num_rows(int64_t num_rows) {
fbb_.AddElement<int64_t>(CTable::VT_NUM_ROWS, num_rows, 0);
}
void add_columns(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Column>>> columns) {
fbb_.AddOffset(CTable::VT_COLUMNS, columns);
}
void add_version(int32_t version) {
fbb_.AddElement<int32_t>(CTable::VT_VERSION, version, 0);
}
void add_metadata(flatbuffers::Offset<flatbuffers::String> metadata) {
fbb_.AddOffset(CTable::VT_METADATA, metadata);
}
explicit CTableBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
CTableBuilder &operator=(const CTableBuilder &);
flatbuffers::Offset<CTable> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<CTable>(end);
return o;
}
};
inline flatbuffers::Offset<CTable> CreateCTable(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> description = 0,
int64_t num_rows = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Column>>> columns = 0,
int32_t version = 0,
flatbuffers::Offset<flatbuffers::String> metadata = 0) {
CTableBuilder builder_(_fbb);
builder_.add_num_rows(num_rows);
builder_.add_metadata(metadata);
builder_.add_version(version);
builder_.add_columns(columns);
builder_.add_description(description);
return builder_.Finish();
}
inline flatbuffers::Offset<CTable> CreateCTableDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *description = nullptr,
int64_t num_rows = 0,
const std::vector<flatbuffers::Offset<Column>> *columns = nullptr,
int32_t version = 0,
const char *metadata = nullptr) {
auto description__ = description ? _fbb.CreateString(description) : 0;
auto columns__ = columns ? _fbb.CreateVector<flatbuffers::Offset<Column>>(*columns) : 0;
auto metadata__ = metadata ? _fbb.CreateString(metadata) : 0;
return arrow::ipc::feather::fbs::CreateCTable(
_fbb,
description__,
num_rows,
columns__,
version,
metadata__);
}
inline bool VerifyTypeMetadata(flatbuffers::Verifier &verifier, const void *obj, TypeMetadata type) {
switch (type) {
case TypeMetadata_NONE: {
return true;
}
case TypeMetadata_CategoryMetadata: {
auto ptr = reinterpret_cast<const CategoryMetadata *>(obj);
return verifier.VerifyTable(ptr);
}
case TypeMetadata_TimestampMetadata: {
auto ptr = reinterpret_cast<const TimestampMetadata *>(obj);
return verifier.VerifyTable(ptr);
}
case TypeMetadata_DateMetadata: {
auto ptr = reinterpret_cast<const DateMetadata *>(obj);
return verifier.VerifyTable(ptr);
}
case TypeMetadata_TimeMetadata: {
auto ptr = reinterpret_cast<const TimeMetadata *>(obj);
return verifier.VerifyTable(ptr);
}
default: return false;
}
}
inline bool VerifyTypeMetadataVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
if (!values || !types) return !values && !types;
if (values->size() != types->size()) return false;
for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
if (!VerifyTypeMetadata(
verifier, values->Get(i), types->GetEnum<TypeMetadata>(i))) {
return false;
}
}
return true;
}
inline const arrow::ipc::feather::fbs::CTable *GetCTable(const void *buf) {
return flatbuffers::GetRoot<arrow::ipc::feather::fbs::CTable>(buf);
}
inline const arrow::ipc::feather::fbs::CTable *GetSizePrefixedCTable(const void *buf) {
return flatbuffers::GetSizePrefixedRoot<arrow::ipc::feather::fbs::CTable>(buf);
}
inline bool VerifyCTableBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<arrow::ipc::feather::fbs::CTable>(nullptr);
}
inline bool VerifySizePrefixedCTableBuffer(
flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<arrow::ipc::feather::fbs::CTable>(nullptr);
}
inline void FinishCTableBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<arrow::ipc::feather::fbs::CTable> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedCTableBuffer(
flatbuffers::FlatBufferBuilder &fbb,
flatbuffers::Offset<arrow::ipc::feather::fbs::CTable> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace fbs
} // namespace feather
} // namespace ipc
} // namespace arrow
#endif // FLATBUFFERS_GENERATED_FEATHER_ARROW_IPC_FEATHER_FBS_H_

View File

@ -84,25 +84,17 @@
<!-- Quota for user. --> <!-- Quota for user. -->
<quota>default</quota> <quota>default</quota>
<!-- For testing the table filters --> <!-- Example of row level security policy. -->
<databases> <!-- <databases>
<test> <test>
<!-- Simple expression filter -->
<filtered_table1> <filtered_table1>
<filter>a = 1</filter> <filter>a = 1</filter>
</filtered_table1> </filtered_table1>
<!-- Complex expression filter -->
<filtered_table2> <filtered_table2>
<filter>a + b &lt; 1 or c - d &gt; 5</filter> <filter>a + b &lt; 1 or c - d &gt; 5</filter>
</filtered_table2> </filtered_table2>
<!-- Filter with ALIAS column -->
<filtered_table3>
<filter>c = 1</filter>
</filtered_table3>
</test> </test>
</databases> </databases> -->
</default> </default>
<!-- Example of user with readonly access. --> <!-- Example of user with readonly access. -->

View File

@ -3,6 +3,7 @@
#include <Access/MemoryAccessStorage.h> #include <Access/MemoryAccessStorage.h>
#include <Access/UsersConfigAccessStorage.h> #include <Access/UsersConfigAccessStorage.h>
#include <Access/QuotaContextFactory.h> #include <Access/QuotaContextFactory.h>
#include <Access/RowPolicyContextFactory.h>
namespace DB namespace DB
@ -21,7 +22,8 @@ namespace
AccessControlManager::AccessControlManager() AccessControlManager::AccessControlManager()
: MultipleAccessStorage(createStorages()), : MultipleAccessStorage(createStorages()),
quota_context_factory(std::make_unique<QuotaContextFactory>(*this)) quota_context_factory(std::make_unique<QuotaContextFactory>(*this)),
row_policy_context_factory(std::make_unique<RowPolicyContextFactory>(*this))
{ {
} }
@ -49,4 +51,11 @@ std::vector<QuotaUsageInfo> AccessControlManager::getQuotaUsageInfo() const
{ {
return quota_context_factory->getUsageInfo(); return quota_context_factory->getUsageInfo();
} }
std::shared_ptr<RowPolicyContext> AccessControlManager::getRowPolicyContext(const String & user_name) const
{
return row_policy_context_factory->createContext(user_name);
}
} }

View File

@ -22,6 +22,8 @@ namespace DB
class QuotaContext; class QuotaContext;
class QuotaContextFactory; class QuotaContextFactory;
struct QuotaUsageInfo; struct QuotaUsageInfo;
class RowPolicyContext;
class RowPolicyContextFactory;
/// Manages access control entities. /// Manages access control entities.
@ -38,8 +40,11 @@ public:
std::vector<QuotaUsageInfo> getQuotaUsageInfo() const; std::vector<QuotaUsageInfo> getQuotaUsageInfo() const;
std::shared_ptr<RowPolicyContext> getRowPolicyContext(const String & user_name) const;
private: private:
std::unique_ptr<QuotaContextFactory> quota_context_factory; std::unique_ptr<QuotaContextFactory> quota_context_factory;
std::unique_ptr<RowPolicyContextFactory> row_policy_context_factory;
}; };
} }

View File

@ -1,5 +1,6 @@
#include <Access/IAccessEntity.h> #include <Access/IAccessEntity.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h>
#include <common/demangle.h> #include <common/demangle.h>
@ -9,6 +10,8 @@ String IAccessEntity::getTypeName(std::type_index type)
{ {
if (type == typeid(Quota)) if (type == typeid(Quota))
return "Quota"; return "Quota";
if (type == typeid(RowPolicy))
return "Row policy";
return demangle(type.name()); return demangle(type.name());
} }

View File

@ -0,0 +1,111 @@
#include <Access/RowPolicy.h>
#include <Interpreters/Context.h>
#include <Common/quoteString.h>
#include <boost/range/algorithm/equal.hpp>
namespace DB
{
namespace
{
void generateFullNameImpl(const String & database_, const String & table_name_, const String & policy_name_, String & full_name_)
{
full_name_.clear();
full_name_.reserve(database_.length() + table_name_.length() + policy_name_.length() + 6);
full_name_ += backQuoteIfNeed(policy_name_);
full_name_ += " ON ";
if (!database_.empty())
{
full_name_ += backQuoteIfNeed(database_);
full_name_ += '.';
}
full_name_ += backQuoteIfNeed(table_name_);
}
}
String RowPolicy::FullNameParts::getFullName() const
{
String full_name;
generateFullNameImpl(database, table_name, policy_name, full_name);
return full_name;
}
String RowPolicy::FullNameParts::getFullName(const Context & context) const
{
String full_name;
generateFullNameImpl(database.empty() ? context.getCurrentDatabase() : database, table_name, policy_name, full_name);
return full_name;
}
void RowPolicy::setDatabase(const String & database_)
{
database = database_;
generateFullNameImpl(database, table_name, policy_name, full_name);
}
void RowPolicy::setTableName(const String & table_name_)
{
table_name = table_name_;
generateFullNameImpl(database, table_name, policy_name, full_name);
}
void RowPolicy::setName(const String & policy_name_)
{
policy_name = policy_name_;
generateFullNameImpl(database, table_name, policy_name, full_name);
}
void RowPolicy::setFullName(const String & database_, const String & table_name_, const String & policy_name_)
{
database = database_;
table_name = table_name_;
policy_name = policy_name_;
generateFullNameImpl(database, table_name, policy_name, full_name);
}
bool RowPolicy::equal(const IAccessEntity & other) const
{
if (!IAccessEntity::equal(other))
return false;
const auto & other_policy = typeid_cast<const RowPolicy &>(other);
return (database == other_policy.database) && (table_name == other_policy.table_name) && (policy_name == other_policy.policy_name)
&& boost::range::equal(conditions, other_policy.conditions) && restrictive == other_policy.restrictive
&& (roles == other_policy.roles) && (all_roles == other_policy.all_roles) && (except_roles == other_policy.except_roles);
}
const char * RowPolicy::conditionIndexToString(ConditionIndex index)
{
switch (index)
{
case SELECT_FILTER: return "SELECT_FILTER";
case INSERT_CHECK: return "INSERT_CHECK";
case UPDATE_FILTER: return "UPDATE_FILTER";
case UPDATE_CHECK: return "UPDATE_CHECK";
case DELETE_FILTER: return "DELETE_FILTER";
}
__builtin_unreachable();
}
const char * RowPolicy::conditionIndexToColumnName(ConditionIndex index)
{
switch (index)
{
case SELECT_FILTER: return "select_filter";
case INSERT_CHECK: return "insert_check";
case UPDATE_FILTER: return "update_filter";
case UPDATE_CHECK: return "update_check";
case DELETE_FILTER: return "delete_filter";
}
__builtin_unreachable();
}
}

View File

@ -0,0 +1,81 @@
#pragma once
#include <Access/IAccessEntity.h>
namespace DB
{
class Context;
/** Represents a row level security policy for a table.
*/
struct RowPolicy : public IAccessEntity
{
void setDatabase(const String & database_);
void setTableName(const String & table_name_);
void setName(const String & policy_name_) override;
void setFullName(const String & database_, const String & table_name_, const String & policy_name_);
String getDatabase() const { return database; }
String getTableName() const { return table_name; }
String getName() const override { return policy_name; }
struct FullNameParts
{
String database;
String table_name;
String policy_name;
String getFullName() const;
String getFullName(const Context & context) const;
};
/// Filter is a SQL conditional expression used to figure out which rows should be visible
/// for user or available for modification. If the expression returns NULL or false for some rows
/// those rows are silently suppressed.
/// Check is a SQL condition expression used to check whether a row can be written into
/// the table. If the expression returns NULL or false an exception is thrown.
/// If a conditional expression here is empty it means no filtering is applied.
enum ConditionIndex
{
SELECT_FILTER,
INSERT_CHECK,
UPDATE_FILTER,
UPDATE_CHECK,
DELETE_FILTER,
};
static constexpr size_t MAX_CONDITION_INDEX = 5;
static const char * conditionIndexToString(ConditionIndex index);
static const char * conditionIndexToColumnName(ConditionIndex index);
String conditions[MAX_CONDITION_INDEX];
/// Sets that the policy is permissive.
/// A row is only accessible if at least one of the permissive policies passes,
/// in addition to all the restrictive policies.
void setPermissive(bool permissive_ = true) { setRestrictive(!permissive_); }
bool isPermissive() const { return !isRestrictive(); }
/// Sets that the policy is restrictive.
/// A row is only accessible if at least one of the permissive policies passes,
/// in addition to all the restrictive policies.
void setRestrictive(bool restrictive_ = true) { restrictive = restrictive_; }
bool isRestrictive() const { return restrictive; }
bool equal(const IAccessEntity & other) const override;
std::shared_ptr<IAccessEntity> clone() const override { return cloneImpl<RowPolicy>(); }
/// Which roles or users should use this quota.
Strings roles;
bool all_roles = false;
Strings except_roles;
private:
String database;
String table_name;
String policy_name;
bool restrictive = false;
};
using RowPolicyPtr = std::shared_ptr<const RowPolicy>;
}

View File

@ -0,0 +1,59 @@
#include <Access/RowPolicyContext.h>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
namespace DB
{
size_t RowPolicyContext::Hash::operator()(const DatabaseAndTableNameRef & database_and_table_name) const
{
return std::hash<StringRef>{}(database_and_table_name.first) - std::hash<StringRef>{}(database_and_table_name.second);
}
RowPolicyContext::RowPolicyContext()
: atomic_map_of_mixed_conditions(std::make_shared<MapOfMixedConditions>())
{
}
RowPolicyContext::~RowPolicyContext() = default;
RowPolicyContext::RowPolicyContext(const String & user_name_)
: user_name(user_name_)
{}
ASTPtr RowPolicyContext::getCondition(const String & database, const String & table_name, ConditionIndex index) const
{
/// We don't lock `mutex` here.
auto map_of_mixed_conditions = std::atomic_load(&atomic_map_of_mixed_conditions);
auto it = map_of_mixed_conditions->find({database, table_name});
if (it == map_of_mixed_conditions->end())
return {};
return it->second.mixed_conditions[index];
}
std::vector<UUID> RowPolicyContext::getCurrentPolicyIDs() const
{
/// We don't lock `mutex` here.
auto map_of_mixed_conditions = std::atomic_load(&atomic_map_of_mixed_conditions);
std::vector<UUID> policy_ids;
for (const auto & mixed_conditions : *map_of_mixed_conditions | boost::adaptors::map_values)
boost::range::copy(mixed_conditions.policy_ids, std::back_inserter(policy_ids));
return policy_ids;
}
std::vector<UUID> RowPolicyContext::getCurrentPolicyIDs(const String & database, const String & table_name) const
{
/// We don't lock `mutex` here.
auto map_of_mixed_conditions = std::atomic_load(&atomic_map_of_mixed_conditions);
auto it = map_of_mixed_conditions->find({database, table_name});
if (it == map_of_mixed_conditions->end())
return {};
return it->second.policy_ids;
}
}

View File

@ -0,0 +1,66 @@
#pragma once
#include <Access/RowPolicy.h>
#include <Core/Types.h>
#include <Core/UUID.h>
#include <common/StringRef.h>
#include <memory>
#include <unordered_map>
namespace DB
{
class IAST;
using ASTPtr = std::shared_ptr<IAST>;
/// Provides fast access to row policies' conditions for a specific user and tables.
class RowPolicyContext
{
public:
/// Default constructor makes a row policy usage context which restricts nothing.
RowPolicyContext();
~RowPolicyContext();
using ConditionIndex = RowPolicy::ConditionIndex;
/// Returns prepared filter for a specific table and operations.
/// The function can return nullptr, that means there is no filters applied.
/// The returned filter can be a combination of the filters defined by multiple row policies.
ASTPtr getCondition(const String & database, const String & table_name, ConditionIndex index) const;
/// Returns IDs of all the policies used by the current user.
std::vector<UUID> getCurrentPolicyIDs() const;
/// Returns IDs of the policies used by a concrete table.
std::vector<UUID> getCurrentPolicyIDs(const String & database, const String & table_name) const;
private:
friend class RowPolicyContextFactory;
friend struct ext::shared_ptr_helper<RowPolicyContext>;
RowPolicyContext(const String & user_name_); /// RowPolicyContext should be created by RowPolicyContextFactory.
using DatabaseAndTableName = std::pair<String, String>;
using DatabaseAndTableNameRef = std::pair<StringRef, StringRef>;
struct Hash
{
size_t operator()(const DatabaseAndTableNameRef & database_and_table_name) const;
};
static constexpr size_t MAX_CONDITION_INDEX = RowPolicy::MAX_CONDITION_INDEX;
using ParsedConditions = std::array<ASTPtr, MAX_CONDITION_INDEX>;
struct MixedConditions
{
std::unique_ptr<DatabaseAndTableName> database_and_table_name_keeper;
ParsedConditions mixed_conditions;
std::vector<UUID> policy_ids;
};
using MapOfMixedConditions = std::unordered_map<DatabaseAndTableNameRef, MixedConditions, Hash>;
const String user_name;
std::shared_ptr<const MapOfMixedConditions> atomic_map_of_mixed_conditions; /// Changed atomically, not protected by `mutex`.
};
using RowPolicyContextPtr = std::shared_ptr<RowPolicyContext>;
}

View File

@ -0,0 +1,314 @@
#include <Access/RowPolicyContextFactory.h>
#include <Access/RowPolicyContext.h>
#include <Access/AccessControlManager.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/parseQuery.h>
#include <Common/Exception.h>
#include <Common/quoteString.h>
#include <ext/range.h>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
namespace DB
{
namespace
{
bool tryGetLiteralBool(const IAST & ast, bool & value)
{
try
{
if (const ASTLiteral * literal = ast.as<ASTLiteral>())
{
value = !literal->value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), literal->value);
return true;
}
return false;
}
catch (...)
{
return false;
}
}
ASTPtr applyFunctionAND(ASTs arguments)
{
bool const_arguments = true;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(*argument, b))
return false;
const_arguments &= b;
return true;
});
if (!const_arguments)
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "and";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
ASTPtr applyFunctionOR(ASTs arguments)
{
bool const_arguments = false;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(*argument, b))
return false;
const_arguments |= b;
return true;
});
if (const_arguments)
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "or";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
using ConditionIndex = RowPolicy::ConditionIndex;
static constexpr size_t MAX_CONDITION_INDEX = RowPolicy::MAX_CONDITION_INDEX;
/// Accumulates conditions from multiple row policies and joins them using the AND logical operation.
class ConditionsMixer
{
public:
void add(const ASTPtr & condition, bool is_restrictive)
{
if (!condition)
return;
if (is_restrictive)
restrictions.push_back(condition);
else
permissions.push_back(condition);
}
ASTPtr getResult() &&
{
/// Process permissive conditions.
if (!permissions.empty())
restrictions.push_back(applyFunctionOR(std::move(permissions)));
/// Process restrictive conditions.
if (!restrictions.empty())
return applyFunctionAND(std::move(restrictions));
return nullptr;
}
private:
ASTs permissions;
ASTs restrictions;
};
}
void RowPolicyContextFactory::PolicyInfo::setPolicy(const RowPolicyPtr & policy_)
{
policy = policy_;
boost::range::copy(policy->roles, std::inserter(roles, roles.end()));
all_roles = policy->all_roles;
boost::range::copy(policy->except_roles, std::inserter(except_roles, except_roles.end()));
for (auto index : ext::range_with_static_cast<ConditionIndex>(0, MAX_CONDITION_INDEX))
{
const String & condition = policy->conditions[index];
auto previous_range = std::pair(std::begin(policy->conditions), std::begin(policy->conditions) + index);
auto previous_it = std::find(previous_range.first, previous_range.second, condition);
if (previous_it != previous_range.second)
{
/// The condition is already parsed before.
parsed_conditions[index] = parsed_conditions[previous_it - previous_range.first];
}
else
{
/// Try to parse the condition.
try
{
ParserExpression parser;
parsed_conditions[index] = parseQuery(parser, condition, 0);
}
catch (...)
{
tryLogCurrentException(
&Poco::Logger::get("RowPolicy"),
String("Could not parse the condition ") + RowPolicy::conditionIndexToString(index) + " of row policy "
+ backQuote(policy->getFullName()));
}
}
}
}
bool RowPolicyContextFactory::PolicyInfo::canUseWithContext(const RowPolicyContext & context) const
{
if (roles.count(context.user_name))
return true;
if (all_roles && !except_roles.count(context.user_name))
return true;
return false;
}
RowPolicyContextFactory::RowPolicyContextFactory(const AccessControlManager & access_control_manager_)
: access_control_manager(access_control_manager_)
{
}
RowPolicyContextFactory::~RowPolicyContextFactory() = default;
RowPolicyContextPtr RowPolicyContextFactory::createContext(const String & user_name)
{
std::lock_guard lock{mutex};
ensureAllRowPoliciesRead();
auto context = ext::shared_ptr_helper<RowPolicyContext>::create(user_name);
contexts.push_back(context);
mixConditionsForContext(*context);
return context;
}
void RowPolicyContextFactory::ensureAllRowPoliciesRead()
{
/// `mutex` is already locked.
if (all_policies_read)
return;
all_policies_read = true;
subscription = access_control_manager.subscribeForChanges<RowPolicy>(
[&](const UUID & id, const AccessEntityPtr & entity)
{
if (entity)
rowPolicyAddedOrChanged(id, typeid_cast<RowPolicyPtr>(entity));
else
rowPolicyRemoved(id);
});
for (const UUID & id : access_control_manager.findAll<RowPolicy>())
{
auto quota = access_control_manager.tryRead<RowPolicy>(id);
if (quota)
all_policies.emplace(id, PolicyInfo(quota));
}
}
void RowPolicyContextFactory::rowPolicyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy)
{
std::lock_guard lock{mutex};
auto it = all_policies.find(policy_id);
if (it == all_policies.end())
{
it = all_policies.emplace(policy_id, PolicyInfo(new_policy)).first;
}
else
{
if (it->second.policy == new_policy)
return;
}
auto & info = it->second;
info.setPolicy(new_policy);
mixConditionsForAllContexts();
}
void RowPolicyContextFactory::rowPolicyRemoved(const UUID & policy_id)
{
std::lock_guard lock{mutex};
all_policies.erase(policy_id);
mixConditionsForAllContexts();
}
void RowPolicyContextFactory::mixConditionsForAllContexts()
{
/// `mutex` is already locked.
boost::range::remove_erase_if(
contexts,
[&](const std::weak_ptr<RowPolicyContext> & weak)
{
auto context = weak.lock();
if (!context)
return true; // remove from the `contexts` list.
mixConditionsForContext(*context);
return false; // keep in the `contexts` list.
});
}
void RowPolicyContextFactory::mixConditionsForContext(RowPolicyContext & context)
{
/// `mutex` is already locked.
struct Mixers
{
ConditionsMixer mixers[MAX_CONDITION_INDEX];
std::vector<UUID> policy_ids;
};
using MapOfMixedConditions = RowPolicyContext::MapOfMixedConditions;
using DatabaseAndTableName = RowPolicyContext::DatabaseAndTableName;
using DatabaseAndTableNameRef = RowPolicyContext::DatabaseAndTableNameRef;
using Hash = RowPolicyContext::Hash;
std::unordered_map<DatabaseAndTableName, Mixers, Hash> map_of_mixers;
for (const auto & [policy_id, info] : all_policies)
{
if (info.canUseWithContext(context))
{
const auto & policy = *info.policy;
auto & mixers = map_of_mixers[std::pair{policy.getDatabase(), policy.getTableName()}];
mixers.policy_ids.push_back(policy_id);
for (auto index : ext::range(0, MAX_CONDITION_INDEX))
mixers.mixers[index].add(info.parsed_conditions[index], policy.isRestrictive());
}
}
auto map_of_mixed_conditions = std::make_shared<MapOfMixedConditions>();
for (auto & [database_and_table_name, mixers] : map_of_mixers)
{
auto database_and_table_name_keeper = std::make_unique<DatabaseAndTableName>();
database_and_table_name_keeper->first = database_and_table_name.first;
database_and_table_name_keeper->second = database_and_table_name.second;
auto & mixed_conditions = (*map_of_mixed_conditions)[DatabaseAndTableNameRef{database_and_table_name_keeper->first,
database_and_table_name_keeper->second}];
mixed_conditions.database_and_table_name_keeper = std::move(database_and_table_name_keeper);
mixed_conditions.policy_ids = std::move(mixers.policy_ids);
for (auto index : ext::range(0, MAX_CONDITION_INDEX))
mixed_conditions.mixed_conditions[index] = std::move(mixers.mixers[index]).getResult();
}
std::atomic_store(&context.atomic_map_of_mixed_conditions, std::shared_ptr<const MapOfMixedConditions>{map_of_mixed_conditions});
}
}

View File

@ -0,0 +1,54 @@
#pragma once
#include <Access/RowPolicyContext.h>
#include <Access/IAccessStorage.h>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
namespace DB
{
class AccessControlManager;
/// Stores read and parsed row policies.
class RowPolicyContextFactory
{
public:
RowPolicyContextFactory(const AccessControlManager & access_control_manager_);
~RowPolicyContextFactory();
RowPolicyContextPtr createContext(const String & user_name);
private:
using ParsedConditions = RowPolicyContext::ParsedConditions;
struct PolicyInfo
{
PolicyInfo(const RowPolicyPtr & policy_) { setPolicy(policy_); }
void setPolicy(const RowPolicyPtr & policy_);
bool canUseWithContext(const RowPolicyContext & context) const;
RowPolicyPtr policy;
std::unordered_set<String> roles;
bool all_roles = false;
std::unordered_set<String> except_roles;
ParsedConditions parsed_conditions;
};
void ensureAllRowPoliciesRead();
void rowPolicyAddedOrChanged(const UUID & policy_id, const RowPolicyPtr & new_policy);
void rowPolicyRemoved(const UUID & policy_id);
void mixConditionsForAllContexts();
void mixConditionsForContext(RowPolicyContext & context);
const AccessControlManager & access_control_manager;
std::unordered_map<UUID, PolicyInfo> all_policies;
bool all_policies_read = false;
IAccessStorage::SubscriptionPtr subscription;
std::vector<std::weak_ptr<RowPolicyContext>> contexts;
std::mutex mutex;
};
}

View File

@ -1,5 +1,6 @@
#include <Access/UsersConfigAccessStorage.h> #include <Access/UsersConfigAccessStorage.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h>
#include <Common/StringUtils/StringUtils.h> #include <Common/StringUtils/StringUtils.h>
#include <Common/quoteString.h> #include <Common/quoteString.h>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
@ -15,6 +16,8 @@ namespace
{ {
if (type == typeid(Quota)) if (type == typeid(Quota))
return 'Q'; return 'Q';
if (type == typeid(RowPolicy))
return 'P';
return 0; return 0;
} }
@ -112,6 +115,57 @@ namespace
} }
return quotas; return quotas;
} }
std::vector<AccessEntityPtr> parseRowPolicies(const Poco::Util::AbstractConfiguration & config, Poco::Logger * log)
{
std::vector<AccessEntityPtr> policies;
Poco::Util::AbstractConfiguration::Keys user_names;
config.keys("users", user_names);
for (const String & user_name : user_names)
{
const String databases_config = "users." + user_name + ".databases";
if (config.has(databases_config))
{
Poco::Util::AbstractConfiguration::Keys databases;
config.keys(databases_config, databases);
/// Read tables within databases
for (const String & database : databases)
{
const String database_config = databases_config + "." + database;
Poco::Util::AbstractConfiguration::Keys table_names;
config.keys(database_config, table_names);
/// Read table properties
for (const String & table_name : table_names)
{
const auto filter_config = database_config + "." + table_name + ".filter";
if (config.has(filter_config))
{
try
{
auto policy = std::make_shared<RowPolicy>();
policy->setFullName(database, table_name, user_name);
policy->conditions[RowPolicy::SELECT_FILTER] = config.getString(filter_config);
policy->roles.push_back(user_name);
policies.push_back(policy);
}
catch (...)
{
tryLogCurrentException(
log,
"Could not parse row policy " + backQuote(user_name) + " on table " + backQuoteIfNeed(database) + "."
+ backQuoteIfNeed(table_name));
}
}
}
}
}
}
return policies;
}
} }
@ -128,6 +182,8 @@ void UsersConfigAccessStorage::loadFromConfig(const Poco::Util::AbstractConfigur
std::vector<std::pair<UUID, AccessEntityPtr>> all_entities; std::vector<std::pair<UUID, AccessEntityPtr>> all_entities;
for (const auto & entity : parseQuotas(config, getLogger())) for (const auto & entity : parseQuotas(config, getLogger()))
all_entities.emplace_back(generateID(*entity), entity); all_entities.emplace_back(generateID(*entity), entity);
for (const auto & entity : parseRowPolicies(config, getLogger()))
all_entities.emplace_back(generateID(*entity), entity);
memory_storage.setAll(all_entities); memory_storage.setAll(all_entities);
} }

View File

@ -24,11 +24,16 @@ struct AggregateFunctionArgMinMaxData
ResultData result; // the argument at which the minimum/maximum value is reached. ResultData result; // the argument at which the minimum/maximum value is reached.
ValueData value; // value for which the minimum/maximum is calculated. ValueData value; // value for which the minimum/maximum is calculated.
static bool allocatesMemoryInArena()
{
return ResultData::allocatesMemoryInArena() || ValueData::allocatesMemoryInArena();
}
}; };
/// Returns the first arg value found for the minimum/maximum value. Example: argMax(arg, value). /// Returns the first arg value found for the minimum/maximum value. Example: argMax(arg, value).
template <typename Data, bool AllocatesMemoryInArena> template <typename Data>
class AggregateFunctionArgMinMax final : public IAggregateFunctionDataHelper<Data, AggregateFunctionArgMinMax<Data, AllocatesMemoryInArena>> class AggregateFunctionArgMinMax final : public IAggregateFunctionDataHelper<Data, AggregateFunctionArgMinMax<Data>>
{ {
private: private:
const DataTypePtr & type_res; const DataTypePtr & type_res;
@ -36,7 +41,7 @@ private:
public: public:
AggregateFunctionArgMinMax(const DataTypePtr & type_res_, const DataTypePtr & type_val_) AggregateFunctionArgMinMax(const DataTypePtr & type_res_, const DataTypePtr & type_val_)
: IAggregateFunctionDataHelper<Data, AggregateFunctionArgMinMax<Data, AllocatesMemoryInArena>>({type_res_, type_val_}, {}), : IAggregateFunctionDataHelper<Data, AggregateFunctionArgMinMax<Data>>({type_res_, type_val_}, {}),
type_res(this->argument_types[0]), type_val(this->argument_types[1]) type_res(this->argument_types[0]), type_val(this->argument_types[1])
{ {
if (!type_val->isComparable()) if (!type_val->isComparable())
@ -77,7 +82,7 @@ public:
bool allocatesMemoryInArena() const override bool allocatesMemoryInArena() const override
{ {
return AllocatesMemoryInArena; return Data::allocatesMemoryInArena();
} }
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override

View File

@ -166,6 +166,11 @@ public:
{ {
return has() && assert_cast<const ColVecType &>(column).getData()[row_num] == value; return has() && assert_cast<const ColVecType &>(column).getData()[row_num] == value;
} }
static bool allocatesMemoryInArena()
{
return false;
}
}; };
@ -384,6 +389,11 @@ public:
{ {
return has() && assert_cast<const ColumnString &>(column).getDataAtWithTerminatingZero(row_num) == getStringRef(); return has() && assert_cast<const ColumnString &>(column).getDataAtWithTerminatingZero(row_num) == getStringRef();
} }
static bool allocatesMemoryInArena()
{
return true;
}
}; };
static_assert( static_assert(
@ -555,6 +565,11 @@ public:
{ {
return has() && to.value == value; return has() && to.value == value;
} }
static bool allocatesMemoryInArena()
{
return false;
}
}; };
@ -675,15 +690,15 @@ struct AggregateFunctionAnyHeavyData : Data
}; };
template <typename Data, bool use_arena> template <typename Data>
class AggregateFunctionsSingleValue final : public IAggregateFunctionDataHelper<Data, AggregateFunctionsSingleValue<Data, use_arena>> class AggregateFunctionsSingleValue final : public IAggregateFunctionDataHelper<Data, AggregateFunctionsSingleValue<Data>>
{ {
private: private:
DataTypePtr & type; DataTypePtr & type;
public: public:
AggregateFunctionsSingleValue(const DataTypePtr & type_) AggregateFunctionsSingleValue(const DataTypePtr & type_)
: IAggregateFunctionDataHelper<Data, AggregateFunctionsSingleValue<Data, use_arena>>({type_}, {}) : IAggregateFunctionDataHelper<Data, AggregateFunctionsSingleValue<Data>>({type_}, {})
, type(this->argument_types[0]) , type(this->argument_types[0])
{ {
if (StringRef(Data::name()) == StringRef("min") if (StringRef(Data::name()) == StringRef("min")
@ -724,7 +739,7 @@ public:
bool allocatesMemoryInArena() const override bool allocatesMemoryInArena() const override
{ {
return use_arena; return Data::allocatesMemoryInArena();
} }
void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override

View File

@ -13,8 +13,8 @@
namespace DB namespace DB
{ {
/// min, max, any, anyLast /// min, max, any, anyLast, anyHeavy, etc...
template <template <typename, bool> class AggregateFunctionTemplate, template <typename> class Data> template <template <typename> class AggregateFunctionTemplate, template <typename> class Data>
static IAggregateFunction * createAggregateFunctionSingleValue(const String & name, const DataTypes & argument_types, const Array & parameters) static IAggregateFunction * createAggregateFunctionSingleValue(const String & name, const DataTypes & argument_types, const Array & parameters)
{ {
assertNoParameters(name, parameters); assertNoParameters(name, parameters);
@ -24,26 +24,26 @@ static IAggregateFunction * createAggregateFunctionSingleValue(const String & na
WhichDataType which(argument_type); WhichDataType which(argument_type);
#define DISPATCH(TYPE) \ #define DISPATCH(TYPE) \
if (which.idx == TypeIndex::TYPE) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<TYPE>>, false>(argument_type); if (which.idx == TypeIndex::TYPE) return new AggregateFunctionTemplate<Data<SingleValueDataFixed<TYPE>>>(argument_type);
FOR_NUMERIC_TYPES(DISPATCH) FOR_NUMERIC_TYPES(DISPATCH)
#undef DISPATCH #undef DISPATCH
if (which.idx == TypeIndex::Date) if (which.idx == TypeIndex::Date)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDate::FieldType>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDate::FieldType>>>(argument_type);
if (which.idx == TypeIndex::DateTime) if (which.idx == TypeIndex::DateTime)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDateTime::FieldType>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DataTypeDateTime::FieldType>>>(argument_type);
if (which.idx == TypeIndex::DateTime64) if (which.idx == TypeIndex::DateTime64)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DateTime64>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<DateTime64>>>(argument_type);
if (which.idx == TypeIndex::Decimal32) if (which.idx == TypeIndex::Decimal32)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal32>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal32>>>(argument_type);
if (which.idx == TypeIndex::Decimal64) if (which.idx == TypeIndex::Decimal64)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal64>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal64>>>(argument_type);
if (which.idx == TypeIndex::Decimal128) if (which.idx == TypeIndex::Decimal128)
return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal128>>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataFixed<Decimal128>>>(argument_type);
if (which.idx == TypeIndex::String) if (which.idx == TypeIndex::String)
return new AggregateFunctionTemplate<Data<SingleValueDataString>, true>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataString>>(argument_type);
return new AggregateFunctionTemplate<Data<SingleValueDataGeneric>, false>(argument_type); return new AggregateFunctionTemplate<Data<SingleValueDataGeneric>>(argument_type);
} }
@ -53,53 +53,28 @@ static IAggregateFunction * createAggregateFunctionArgMinMaxSecond(const DataTyp
{ {
WhichDataType which(val_type); WhichDataType which(val_type);
/// If at least one argument is a String, the function can allocate memory in Arena.
bool is_res_type_string = WhichDataType(res_type).isString();
#define DISPATCH(TYPE) \ #define DISPATCH(TYPE) \
if (which.idx == TypeIndex::TYPE && !is_res_type_string) \ if (which.idx == TypeIndex::TYPE) \
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<TYPE>>>, false>(res_type, val_type); \ return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<TYPE>>>>(res_type, val_type); \
if (which.idx == TypeIndex::TYPE && is_res_type_string) \
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<TYPE>>>, true>(res_type, val_type);
FOR_NUMERIC_TYPES(DISPATCH) FOR_NUMERIC_TYPES(DISPATCH)
#undef DISPATCH #undef DISPATCH
if (!is_res_type_string) if (which.idx == TypeIndex::Date)
{ return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDate::FieldType>>>>(res_type, val_type);
if (which.idx == TypeIndex::Date) if (which.idx == TypeIndex::DateTime)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDate::FieldType>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDateTime::FieldType>>>>(res_type, val_type);
if (which.idx == TypeIndex::DateTime) if (which.idx == TypeIndex::DateTime64)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDateTime::FieldType>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DateTime64>>>>(res_type, val_type);
if (which.idx == TypeIndex::DateTime64) if (which.idx == TypeIndex::Decimal32)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DateTime64>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal32>>>>(res_type, val_type);
if (which.idx == TypeIndex::Decimal32) if (which.idx == TypeIndex::Decimal64)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal32>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal64>>>>(res_type, val_type);
if (which.idx == TypeIndex::Decimal64) if (which.idx == TypeIndex::Decimal128)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal64>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal128>>>>(res_type, val_type);
if (which.idx == TypeIndex::Decimal128) if (which.idx == TypeIndex::String)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal128>>>, false>(res_type, val_type); return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataString>>>(res_type, val_type);
if (which.idx == TypeIndex::String)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataString>>, true>(res_type, val_type);
}
else
{
if (which.idx == TypeIndex::Date)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDate::FieldType>>>, true>(res_type, val_type);
if (which.idx == TypeIndex::DateTime)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DataTypeDateTime::FieldType>>>, true>(res_type, val_type);
if (which.idx == TypeIndex::DateTime64)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<DateTime64>>>, true>(res_type, val_type);
if (which.idx == TypeIndex::Decimal32)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal32>>>, true>(res_type, val_type);
if (which.idx == TypeIndex::Decimal64)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal64>>>, true>(res_type, val_type);
if (which.idx == TypeIndex::Decimal128)
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataFixed<Decimal128>>>, true>(res_type, val_type);
}
/// But generic implementation doesn't allocate memory in Arena. return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataGeneric>>>(res_type, val_type);
return new AggregateFunctionArgMinMax<AggregateFunctionArgMinMaxData<ResData, MinMaxData<SingleValueDataGeneric>>, false>(res_type, val_type);
} }
template <template <typename> class MinMaxData> template <template <typename> class MinMaxData>

View File

@ -261,6 +261,10 @@ void PointInPolygonWithGrid<CoordinateType>::buildGrid()
for (size_t row = 0; row < grid_size; ++row) for (size_t row = 0; row < grid_size; ++row)
{ {
#pragma GCC diagnostic push
#if !__clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
CoordinateType y_min = min_corner.y() + row * cell_height; CoordinateType y_min = min_corner.y() + row * cell_height;
CoordinateType y_max = min_corner.y() + (row + 1) * cell_height; CoordinateType y_max = min_corner.y() + (row + 1) * cell_height;
@ -268,6 +272,7 @@ void PointInPolygonWithGrid<CoordinateType>::buildGrid()
{ {
CoordinateType x_min = min_corner.x() + col * cell_width; CoordinateType x_min = min_corner.x() + col * cell_width;
CoordinateType x_max = min_corner.x() + (col + 1) * cell_width; CoordinateType x_max = min_corner.x() + (col + 1) * cell_width;
#pragma GCC diagnostic pop
Box cell_box(Point(x_min, y_min), Point(x_max, y_max)); Box cell_box(Point(x_min, y_min), Point(x_max, y_max));
Polygon cell_bound; Polygon cell_bound;

View File

@ -0,0 +1,225 @@
#include <Functions/IFunction.h>
#include <Functions/FunctionFactory.h>
#include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypeTuple.h>
#include <DataTypes/DataTypeUUID.h>
#include <Columns/ColumnArray.h>
#include <Columns/ColumnConst.h>
#include <Columns/ColumnString.h>
#include <Columns/ColumnTuple.h>
#include <Interpreters/Context.h>
#include <Access/RowPolicyContext.h>
#include <Access/AccessControlManager.h>
#include <ext/range.h>
namespace DB
{
namespace ErrorCodes
{
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}
/// The currentRowPolicies() function can be called with 0..2 arguments:
/// currentRowPolicies() returns array of tuples (database, table_name, row_policy_name) for all the row policies applied for the current user;
/// currentRowPolicies(table_name) is equivalent to currentRowPolicies(currentDatabase(), table_name);
/// currentRowPolicies(database, table_name) returns array of names of the row policies applied to a specific table and for the current user.
class FunctionCurrentRowPolicies : public IFunction
{
public:
static constexpr auto name = "currentRowPolicies";
static FunctionPtr create(const Context & context_) { return std::make_shared<FunctionCurrentRowPolicies>(context_); }
explicit FunctionCurrentRowPolicies(const Context & context_) : context(context_) {}
String getName() const override { return name; }
size_t getNumberOfArguments() const override { return 0; }
bool isVariadic() const override { return true; }
void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override
{
if (number_of_arguments > 2)
throw Exception("Number of arguments for function " + String(name) + " doesn't match: passed "
+ toString(number_of_arguments) + ", should be 0..2",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
}
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
if (arguments.empty())
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeTuple>(
DataTypes{std::make_shared<DataTypeString>(), std::make_shared<DataTypeString>(), std::make_shared<DataTypeString>()}));
else
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>());
}
bool isDeterministic() const override { return false; }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_pos, size_t input_rows_count) override
{
if (arguments.empty())
{
auto database_column = ColumnString::create();
auto table_name_column = ColumnString::create();
auto policy_name_column = ColumnString::create();
for (const auto & policy_id : context.getRowPolicy()->getCurrentPolicyIDs())
{
const auto policy = context.getAccessControlManager().tryRead<RowPolicy>(policy_id);
if (policy)
{
const String database = policy->getDatabase();
const String table_name = policy->getTableName();
const String policy_name = policy->getName();
database_column->insertData(database.data(), database.length());
table_name_column->insertData(table_name.data(), table_name.length());
policy_name_column->insertData(policy_name.data(), policy_name.length());
}
}
auto offset_column = ColumnArray::ColumnOffsets::create();
offset_column->insertValue(policy_name_column->size());
block.getByPosition(result_pos).column = ColumnConst::create(
ColumnArray::create(
ColumnTuple::create(Columns{std::move(database_column), std::move(table_name_column), std::move(policy_name_column)}),
std::move(offset_column)),
input_rows_count);
return;
}
const IColumn * database_column = nullptr;
if (arguments.size() == 2)
{
const auto & database_column_with_type = block.getByPosition(arguments[0]);
if (!isStringOrFixedString(database_column_with_type.type))
throw Exception{"The first argument of function " + String(name)
+ " should be a string containing database name, illegal type: "
+ database_column_with_type.type->getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
database_column = database_column_with_type.column.get();
}
const auto & table_name_column_with_type = block.getByPosition(arguments[arguments.size() - 1]);
if (!isStringOrFixedString(table_name_column_with_type.type))
throw Exception{"The" + String(database_column ? " last" : "") + " argument of function " + String(name)
+ " should be a string containing table name, illegal type: " + table_name_column_with_type.type->getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
const IColumn * table_name_column = table_name_column_with_type.column.get();
auto policy_name_column = ColumnString::create();
auto offset_column = ColumnArray::ColumnOffsets::create();
for (const auto i : ext::range(0, input_rows_count))
{
String database = database_column ? database_column->getDataAt(i).toString() : context.getCurrentDatabase();
String table_name = table_name_column->getDataAt(i).toString();
for (const auto & policy_id : context.getRowPolicy()->getCurrentPolicyIDs(database, table_name))
{
const auto policy = context.getAccessControlManager().tryRead<RowPolicy>(policy_id);
if (policy)
{
const String policy_name = policy->getName();
policy_name_column->insertData(policy_name.data(), policy_name.length());
}
}
offset_column->insertValue(policy_name_column->size());
}
block.getByPosition(result_pos).column = ColumnArray::create(std::move(policy_name_column), std::move(offset_column));
}
private:
const Context & context;
};
/// The currentRowPolicyIDs() function can be called with 0..2 arguments:
/// currentRowPolicyIDs() returns array of IDs of all the row policies applied for the current user;
/// currentRowPolicyIDs(table_name) is equivalent to currentRowPolicyIDs(currentDatabase(), table_name);
/// currentRowPolicyIDs(database, table_name) returns array of IDs of the row policies applied to a specific table and for the current user.
class FunctionCurrentRowPolicyIDs : public IFunction
{
public:
static constexpr auto name = "currentRowPolicyIDs";
static FunctionPtr create(const Context & context_) { return std::make_shared<FunctionCurrentRowPolicyIDs>(context_); }
explicit FunctionCurrentRowPolicyIDs(const Context & context_) : context(context_) {}
String getName() const override { return name; }
size_t getNumberOfArguments() const override { return 0; }
bool isVariadic() const override { return true; }
void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override
{
if (number_of_arguments > 2)
throw Exception("Number of arguments for function " + String(name) + " doesn't match: passed "
+ toString(number_of_arguments) + ", should be 0..2",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
}
DataTypePtr getReturnTypeImpl(const DataTypes & /* arguments */) const override
{
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeUUID>());
}
bool isDeterministic() const override { return false; }
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_pos, size_t input_rows_count) override
{
if (arguments.empty())
{
auto policy_id_column = ColumnVector<UInt128>::create();
for (const auto & policy_id : context.getRowPolicy()->getCurrentPolicyIDs())
policy_id_column->insertValue(policy_id);
auto offset_column = ColumnArray::ColumnOffsets::create();
offset_column->insertValue(policy_id_column->size());
block.getByPosition(result_pos).column
= ColumnConst::create(ColumnArray::create(std::move(policy_id_column), std::move(offset_column)), input_rows_count);
return;
}
const IColumn * database_column = nullptr;
if (arguments.size() == 2)
{
const auto & database_column_with_type = block.getByPosition(arguments[0]);
if (!isStringOrFixedString(database_column_with_type.type))
throw Exception{"The first argument of function " + String(name)
+ " should be a string containing database name, illegal type: "
+ database_column_with_type.type->getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
database_column = database_column_with_type.column.get();
}
const auto & table_name_column_with_type = block.getByPosition(arguments[arguments.size() - 1]);
if (!isStringOrFixedString(table_name_column_with_type.type))
throw Exception{"The" + String(database_column ? " last" : "") + " argument of function " + String(name)
+ " should be a string containing table name, illegal type: " + table_name_column_with_type.type->getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
const IColumn * table_name_column = table_name_column_with_type.column.get();
auto policy_id_column = ColumnVector<UInt128>::create();
auto offset_column = ColumnArray::ColumnOffsets::create();
for (const auto i : ext::range(0, input_rows_count))
{
String database = database_column ? database_column->getDataAt(i).toString() : context.getCurrentDatabase();
String table_name = table_name_column->getDataAt(i).toString();
for (const auto & policy_id : context.getRowPolicy()->getCurrentPolicyIDs(database, table_name))
policy_id_column->insertValue(policy_id);
offset_column->insertValue(policy_id_column->size());
}
block.getByPosition(result_pos).column = ColumnArray::create(std::move(policy_id_column), std::move(offset_column));
}
private:
const Context & context;
};
void registerFunctionCurrentRowPolicies(FunctionFactory & factory)
{
factory.registerFunction<FunctionCurrentRowPolicies>();
factory.registerFunction<FunctionCurrentRowPolicyIDs>();
}
}

View File

@ -9,6 +9,7 @@ class FunctionFactory;
void registerFunctionCurrentDatabase(FunctionFactory &); void registerFunctionCurrentDatabase(FunctionFactory &);
void registerFunctionCurrentUser(FunctionFactory &); void registerFunctionCurrentUser(FunctionFactory &);
void registerFunctionCurrentQuota(FunctionFactory &); void registerFunctionCurrentQuota(FunctionFactory &);
void registerFunctionCurrentRowPolicies(FunctionFactory &);
void registerFunctionHostName(FunctionFactory &); void registerFunctionHostName(FunctionFactory &);
void registerFunctionFQDN(FunctionFactory &); void registerFunctionFQDN(FunctionFactory &);
void registerFunctionVisibleWidth(FunctionFactory &); void registerFunctionVisibleWidth(FunctionFactory &);

View File

@ -8,6 +8,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory)
registerFunctionCurrentDatabase(factory); registerFunctionCurrentDatabase(factory);
registerFunctionCurrentUser(factory); registerFunctionCurrentUser(factory);
registerFunctionCurrentQuota(factory); registerFunctionCurrentQuota(factory);
registerFunctionCurrentRowPolicies(factory);
registerFunctionHostName(factory); registerFunctionHostName(factory);
registerFunctionFQDN(factory); registerFunctionFQDN(factory);
registerFunctionVisibleWidth(factory); registerFunctionVisibleWidth(factory);

View File

@ -28,6 +28,7 @@
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/SettingsConstraints.h> #include <Access/SettingsConstraints.h>
#include <Access/QuotaContext.h> #include <Access/QuotaContext.h>
#include <Access/RowPolicyContext.h>
#include <Interpreters/ExpressionJIT.h> #include <Interpreters/ExpressionJIT.h>
#include <Interpreters/UsersManager.h> #include <Interpreters/UsersManager.h>
#include <Dictionaries/Embedded/GeoDictionariesLoader.h> #include <Dictionaries/Embedded/GeoDictionariesLoader.h>
@ -333,6 +334,7 @@ Context Context::createGlobal()
{ {
Context res; Context res;
res.quota = std::make_shared<QuotaContext>(); res.quota = std::make_shared<QuotaContext>();
res.row_policy = std::make_shared<RowPolicyContext>();
res.shared = std::make_shared<ContextShared>(); res.shared = std::make_shared<ContextShared>();
return res; return res;
} }
@ -625,6 +627,13 @@ void Context::checkQuotaManagementIsAllowed()
"User " + client_info.current_user + " doesn't have enough privileges to manage quotas", ErrorCodes::NOT_ENOUGH_PRIVILEGES); "User " + client_info.current_user + " doesn't have enough privileges to manage quotas", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
} }
void Context::checkRowPolicyManagementIsAllowed()
{
if (!is_row_policy_management_allowed)
throw Exception(
"User " + client_info.current_user + " doesn't have enough privileges to manage row policies", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
}
void Context::setUsersConfig(const ConfigurationPtr & config) void Context::setUsersConfig(const ConfigurationPtr & config)
{ {
auto lock = getLock(); auto lock = getLock();
@ -639,34 +648,6 @@ ConfigurationPtr Context::getUsersConfig()
return shared->users_config; return shared->users_config;
} }
bool Context::hasUserProperty(const String & database, const String & table, const String & name) const
{
auto lock = getLock();
// No user - no properties.
if (client_info.current_user.empty())
return false;
const auto & props = shared->users_manager->getUser(client_info.current_user)->table_props;
auto db = props.find(database);
if (db == props.end())
return false;
auto table_props = db->second.find(table);
if (table_props == db->second.end())
return false;
return !!table_props->second.count(name);
}
const String & Context::getUserProperty(const String & database, const String & table, const String & name) const
{
auto lock = getLock();
const auto & props = shared->users_manager->getUser(client_info.current_user)->table_props;
return props.at(database).at(table).at(name);
}
void Context::calculateUserSettings() void Context::calculateUserSettings()
{ {
auto lock = getLock(); auto lock = getLock();
@ -691,6 +672,8 @@ void Context::calculateUserSettings()
quota = getAccessControlManager().createQuotaContext( quota = getAccessControlManager().createQuotaContext(
client_info.current_user, client_info.current_address.host(), client_info.quota_key); client_info.current_user, client_info.current_address.host(), client_info.quota_key);
is_quota_management_allowed = user->is_quota_management_allowed; is_quota_management_allowed = user->is_quota_management_allowed;
row_policy = getAccessControlManager().getRowPolicyContext(client_info.current_user);
is_row_policy_management_allowed = user->is_row_policy_management_allowed;
} }

View File

@ -45,6 +45,7 @@ namespace DB
struct ContextShared; struct ContextShared;
class Context; class Context;
class QuotaContext; class QuotaContext;
class RowPolicyContext;
class EmbeddedDictionaries; class EmbeddedDictionaries;
class ExternalDictionariesLoader; class ExternalDictionariesLoader;
class ExternalModelsLoader; class ExternalModelsLoader;
@ -140,6 +141,8 @@ private:
std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits. std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits.
bool is_quota_management_allowed = false; /// Whether the current user is allowed to manage quotas via SQL commands. bool is_quota_management_allowed = false; /// Whether the current user is allowed to manage quotas via SQL commands.
std::shared_ptr<RowPolicyContext> row_policy;
bool is_row_policy_management_allowed = false; /// Whether the current user is allowed to manage row policies via SQL commands.
String current_database; String current_database;
Settings settings; /// Setting for query execution. Settings settings; /// Setting for query execution.
std::shared_ptr<const SettingsConstraints> settings_constraints; std::shared_ptr<const SettingsConstraints> settings_constraints;
@ -209,6 +212,8 @@ public:
const AccessControlManager & getAccessControlManager() const; const AccessControlManager & getAccessControlManager() const;
std::shared_ptr<QuotaContext> getQuota() const { return quota; } std::shared_ptr<QuotaContext> getQuota() const { return quota; }
void checkQuotaManagementIsAllowed(); void checkQuotaManagementIsAllowed();
std::shared_ptr<RowPolicyContext> getRowPolicy() const { return row_policy; }
void checkRowPolicyManagementIsAllowed();
/** Take the list of users, quotas and configuration profiles from this config. /** Take the list of users, quotas and configuration profiles from this config.
* The list of users is completely replaced. * The list of users is completely replaced.
@ -217,10 +222,6 @@ public:
void setUsersConfig(const ConfigurationPtr & config); void setUsersConfig(const ConfigurationPtr & config);
ConfigurationPtr getUsersConfig(); ConfigurationPtr getUsersConfig();
// User property is a key-value pair from the configuration entry: users.<username>.databases.<db_name>.<table_name>.<key_name>
bool hasUserProperty(const String & database, const String & table, const String & name) const;
const String & getUserProperty(const String & database, const String & table, const String & name) const;
/// Must be called before getClientInfo. /// Must be called before getClientInfo.
void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key); void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key);

View File

@ -0,0 +1,93 @@
#include <Interpreters/InterpreterCreateRowPolicyQuery.h>
#include <Parsers/ASTCreateRowPolicyQuery.h>
#include <Parsers/ASTRoleList.h>
#include <Parsers/formatAST.h>
#include <Interpreters/Context.h>
#include <Access/AccessControlManager.h>
#include <boost/range/algorithm/sort.hpp>
namespace DB
{
BlockIO InterpreterCreateRowPolicyQuery::execute()
{
context.checkRowPolicyManagementIsAllowed();
const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>();
auto & access_control = context.getAccessControlManager();
if (query.alter)
{
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
{
auto updated_policy = typeid_cast<std::shared_ptr<RowPolicy>>(entity->clone());
updateRowPolicyFromQuery(*updated_policy, query);
return updated_policy;
};
String full_name = query.name_parts.getFullName(context);
if (query.if_exists)
{
if (auto id = access_control.find<RowPolicy>(full_name))
access_control.tryUpdate(*id, update_func);
}
else
access_control.update(access_control.getID<RowPolicy>(full_name), update_func);
}
else
{
auto new_policy = std::make_shared<RowPolicy>();
updateRowPolicyFromQuery(*new_policy, query);
if (query.if_not_exists)
access_control.tryInsert(new_policy);
else if (query.or_replace)
access_control.insertOrReplace(new_policy);
else
access_control.insert(new_policy);
}
return {};
}
void InterpreterCreateRowPolicyQuery::updateRowPolicyFromQuery(RowPolicy & policy, const ASTCreateRowPolicyQuery & query)
{
if (query.alter)
{
if (!query.new_policy_name.empty())
policy.setName(query.new_policy_name);
}
else
{
policy.setDatabase(query.name_parts.database.empty() ? context.getCurrentDatabase() : query.name_parts.database);
policy.setTableName(query.name_parts.table_name);
policy.setName(query.name_parts.policy_name);
}
if (query.is_restrictive)
policy.setRestrictive(*query.is_restrictive);
for (const auto & [index, condition] : query.conditions)
policy.conditions[index] = condition ? serializeAST(*condition) : String{};
if (query.roles)
{
const auto & query_roles = *query.roles;
/// We keep `roles` sorted.
policy.roles = query_roles.roles;
if (query_roles.current_user)
policy.roles.push_back(context.getClientInfo().current_user);
boost::range::sort(policy.roles);
policy.roles.erase(std::unique(policy.roles.begin(), policy.roles.end()), policy.roles.end());
policy.all_roles = query_roles.all_roles;
/// We keep `except_roles` sorted.
policy.except_roles = query_roles.except_roles;
if (query_roles.except_current_user)
policy.except_roles.push_back(context.getClientInfo().current_user);
boost::range::sort(policy.except_roles);
policy.except_roles.erase(std::unique(policy.except_roles.begin(), policy.except_roles.end()), policy.except_roles.end());
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <Interpreters/IInterpreter.h>
#include <Parsers/IAST_fwd.h>
namespace DB
{
class ASTCreateRowPolicyQuery;
struct RowPolicy;
class InterpreterCreateRowPolicyQuery : public IInterpreter
{
public:
InterpreterCreateRowPolicyQuery(const ASTPtr & query_ptr_, Context & context_) : query_ptr(query_ptr_), context(context_) {}
BlockIO execute() override;
private:
void updateRowPolicyFromQuery(RowPolicy & policy, const ASTCreateRowPolicyQuery & query);
ASTPtr query_ptr;
Context & context;
};
}

View File

@ -3,6 +3,8 @@
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/Quota.h> #include <Access/Quota.h>
#include <Access/RowPolicy.h>
#include <boost/range/algorithm/transform.hpp>
namespace DB namespace DB
@ -24,6 +26,19 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
access_control.remove(access_control.getIDs<Quota>(query.names)); access_control.remove(access_control.getIDs<Quota>(query.names));
return {}; return {};
} }
case Kind::ROW_POLICY:
{
context.checkRowPolicyManagementIsAllowed();
Strings full_names;
boost::range::transform(
query.row_policies_names, std::back_inserter(full_names),
[this](const RowPolicy::FullNameParts & row_policy_name) { return row_policy_name.getFullName(context); });
if (query.if_exists)
access_control.tryRemove(access_control.find<RowPolicy>(full_names));
else
access_control.remove(access_control.getIDs<RowPolicy>(full_names));
return {};
}
} }
__builtin_unreachable(); __builtin_unreachable();

View File

@ -2,6 +2,7 @@
#include <Parsers/ASTCheckQuery.h> #include <Parsers/ASTCheckQuery.h>
#include <Parsers/ASTCreateQuery.h> #include <Parsers/ASTCreateQuery.h>
#include <Parsers/ASTCreateQuotaQuery.h> #include <Parsers/ASTCreateQuotaQuery.h>
#include <Parsers/ASTCreateRowPolicyQuery.h>
#include <Parsers/ASTDropAccessEntityQuery.h> #include <Parsers/ASTDropAccessEntityQuery.h>
#include <Parsers/ASTDropQuery.h> #include <Parsers/ASTDropQuery.h>
#include <Parsers/ASTInsertQuery.h> #include <Parsers/ASTInsertQuery.h>
@ -14,6 +15,7 @@
#include <Parsers/ASTShowCreateAccessEntityQuery.h> #include <Parsers/ASTShowCreateAccessEntityQuery.h>
#include <Parsers/ASTShowProcesslistQuery.h> #include <Parsers/ASTShowProcesslistQuery.h>
#include <Parsers/ASTShowQuotasQuery.h> #include <Parsers/ASTShowQuotasQuery.h>
#include <Parsers/ASTShowRowPoliciesQuery.h>
#include <Parsers/ASTShowTablesQuery.h> #include <Parsers/ASTShowTablesQuery.h>
#include <Parsers/ASTUseQuery.h> #include <Parsers/ASTUseQuery.h>
#include <Parsers/ASTExplainQuery.h> #include <Parsers/ASTExplainQuery.h>
@ -24,6 +26,7 @@
#include <Interpreters/InterpreterCheckQuery.h> #include <Interpreters/InterpreterCheckQuery.h>
#include <Interpreters/InterpreterCreateQuery.h> #include <Interpreters/InterpreterCreateQuery.h>
#include <Interpreters/InterpreterCreateQuotaQuery.h> #include <Interpreters/InterpreterCreateQuotaQuery.h>
#include <Interpreters/InterpreterCreateRowPolicyQuery.h>
#include <Interpreters/InterpreterDescribeQuery.h> #include <Interpreters/InterpreterDescribeQuery.h>
#include <Interpreters/InterpreterExplainQuery.h> #include <Interpreters/InterpreterExplainQuery.h>
#include <Interpreters/InterpreterDropAccessEntityQuery.h> #include <Interpreters/InterpreterDropAccessEntityQuery.h>
@ -41,6 +44,7 @@
#include <Interpreters/InterpreterShowCreateQuery.h> #include <Interpreters/InterpreterShowCreateQuery.h>
#include <Interpreters/InterpreterShowProcesslistQuery.h> #include <Interpreters/InterpreterShowProcesslistQuery.h>
#include <Interpreters/InterpreterShowQuotasQuery.h> #include <Interpreters/InterpreterShowQuotasQuery.h>
#include <Interpreters/InterpreterShowRowPoliciesQuery.h>
#include <Interpreters/InterpreterShowTablesQuery.h> #include <Interpreters/InterpreterShowTablesQuery.h>
#include <Interpreters/InterpreterSystemQuery.h> #include <Interpreters/InterpreterSystemQuery.h>
#include <Interpreters/InterpreterUseQuery.h> #include <Interpreters/InterpreterUseQuery.h>
@ -199,6 +203,10 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
{ {
return std::make_unique<InterpreterCreateQuotaQuery>(query, context); return std::make_unique<InterpreterCreateQuotaQuery>(query, context);
} }
else if (query->as<ASTCreateRowPolicyQuery>())
{
return std::make_unique<InterpreterCreateRowPolicyQuery>(query, context);
}
else if (query->as<ASTDropAccessEntityQuery>()) else if (query->as<ASTDropAccessEntityQuery>())
{ {
return std::make_unique<InterpreterDropAccessEntityQuery>(query, context); return std::make_unique<InterpreterDropAccessEntityQuery>(query, context);
@ -211,6 +219,10 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
{ {
return std::make_unique<InterpreterShowQuotasQuery>(query, context); return std::make_unique<InterpreterShowQuotasQuery>(query, context);
} }
else if (query->as<ASTShowRowPoliciesQuery>())
{
return std::make_unique<InterpreterShowRowPoliciesQuery>(query, context);
}
else else
throw Exception("Unknown type of query: " + query->getID(), ErrorCodes::UNKNOWN_TYPE_OF_QUERY); throw Exception("Unknown type of query: " + query->getID(), ErrorCodes::UNKNOWN_TYPE_OF_QUERY);
} }

View File

@ -38,6 +38,8 @@
#include <Parsers/ExpressionListParsers.h> #include <Parsers/ExpressionListParsers.h>
#include <Parsers/parseQuery.h> #include <Parsers/parseQuery.h>
#include <Access/RowPolicyContext.h>
#include <Interpreters/InterpreterSelectQuery.h> #include <Interpreters/InterpreterSelectQuery.h>
#include <Interpreters/InterpreterSelectWithUnionQuery.h> #include <Interpreters/InterpreterSelectWithUnionQuery.h>
#include <Interpreters/InterpreterSetQuery.h> #include <Interpreters/InterpreterSetQuery.h>
@ -118,11 +120,10 @@ namespace
{ {
/// Assumes `storage` is set and the table filter (row-level security) is not empty. /// Assumes `storage` is set and the table filter (row-level security) is not empty.
String generateFilterActions(ExpressionActionsPtr & actions, const StoragePtr & storage, const Context & context, const Names & prerequisite_columns = {}) String generateFilterActions(ExpressionActionsPtr & actions, const Context & context, const StoragePtr & storage, const ASTPtr & row_policy_filter, const Names & prerequisite_columns = {})
{ {
const auto & db_name = storage->getDatabaseName(); const auto & db_name = storage->getDatabaseName();
const auto & table_name = storage->getTableName(); const auto & table_name = storage->getTableName();
const auto & filter_str = context.getUserProperty(db_name, table_name, "filter");
/// TODO: implement some AST builders for this kind of stuff /// TODO: implement some AST builders for this kind of stuff
ASTPtr query_ast = std::make_shared<ASTSelectQuery>(); ASTPtr query_ast = std::make_shared<ASTSelectQuery>();
@ -131,18 +132,15 @@ String generateFilterActions(ExpressionActionsPtr & actions, const StoragePtr &
select_ast->setExpression(ASTSelectQuery::Expression::SELECT, std::make_shared<ASTExpressionList>()); select_ast->setExpression(ASTSelectQuery::Expression::SELECT, std::make_shared<ASTExpressionList>());
auto expr_list = select_ast->select(); auto expr_list = select_ast->select();
auto parseExpression = [] (const String & expr)
{
ParserExpression expr_parser;
return parseQuery(expr_parser, expr, 0);
};
// The first column is our filter expression. // The first column is our filter expression.
expr_list->children.push_back(parseExpression(filter_str)); expr_list->children.push_back(row_policy_filter);
/// Keep columns that are required after the filter actions. /// Keep columns that are required after the filter actions.
for (const auto & column_str : prerequisite_columns) for (const auto & column_str : prerequisite_columns)
expr_list->children.push_back(parseExpression(column_str)); {
ParserExpression expr_parser;
expr_list->children.push_back(parseQuery(expr_parser, column_str, 0));
}
select_ast->setExpression(ASTSelectQuery::Expression::TABLES, std::make_shared<ASTTablesInSelectQuery>()); select_ast->setExpression(ASTSelectQuery::Expression::TABLES, std::make_shared<ASTTablesInSelectQuery>());
auto tables = select_ast->tables(); auto tables = select_ast->tables();
@ -372,10 +370,11 @@ InterpreterSelectQuery::InterpreterSelectQuery(
source_header = storage->getSampleBlockForColumns(required_columns); source_header = storage->getSampleBlockForColumns(required_columns);
/// Fix source_header for filter actions. /// Fix source_header for filter actions.
if (context->hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter")) auto row_policy_filter = context->getRowPolicy()->getCondition(storage->getDatabaseName(), storage->getTableName(), RowPolicy::SELECT_FILTER);
if (row_policy_filter)
{ {
filter_info = std::make_shared<FilterInfo>(); filter_info = std::make_shared<FilterInfo>();
filter_info->column_name = generateFilterActions(filter_info->actions, storage, *context, required_columns); filter_info->column_name = generateFilterActions(filter_info->actions, *context, storage, row_policy_filter, required_columns);
source_header = storage->getSampleBlockForColumns(filter_info->actions->getRequiredColumns()); source_header = storage->getSampleBlockForColumns(filter_info->actions->getRequiredColumns());
} }
} }
@ -496,7 +495,7 @@ Block InterpreterSelectQuery::getSampleBlockImpl()
/// PREWHERE optimization. /// PREWHERE optimization.
/// Turn off, if the table filter (row-level security) is applied. /// Turn off, if the table filter (row-level security) is applied.
if (storage && !context->hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter")) if (storage && !context->getRowPolicy()->getCondition(storage->getDatabaseName(), storage->getTableName(), RowPolicy::SELECT_FILTER))
{ {
query_analyzer->makeSetsForIndex(query.where()); query_analyzer->makeSetsForIndex(query.where());
query_analyzer->makeSetsForIndex(query.prewhere()); query_analyzer->makeSetsForIndex(query.prewhere());
@ -1357,11 +1356,12 @@ void InterpreterSelectQuery::executeFetchColumns(
if (storage) if (storage)
{ {
/// Append columns from the table filter to required /// Append columns from the table filter to required
if (context->hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter")) auto row_policy_filter = context->getRowPolicy()->getCondition(storage->getDatabaseName(), storage->getTableName(), RowPolicy::SELECT_FILTER);
if (row_policy_filter)
{ {
auto initial_required_columns = required_columns; auto initial_required_columns = required_columns;
ExpressionActionsPtr actions; ExpressionActionsPtr actions;
generateFilterActions(actions, storage, *context, initial_required_columns); generateFilterActions(actions, *context, storage, row_policy_filter, initial_required_columns);
auto required_columns_from_filter = actions->getRequiredColumns(); auto required_columns_from_filter = actions->getRequiredColumns();
for (const auto & column : required_columns_from_filter) for (const auto & column : required_columns_from_filter)

View File

@ -1,9 +1,12 @@
#include <Interpreters/InterpreterShowCreateAccessEntityQuery.h> #include <Interpreters/InterpreterShowCreateAccessEntityQuery.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Parsers/ASTCreateQuotaQuery.h> #include <Parsers/ASTCreateQuotaQuery.h>
#include <Parsers/ASTCreateRowPolicyQuery.h>
#include <Parsers/ASTShowCreateAccessEntityQuery.h> #include <Parsers/ASTShowCreateAccessEntityQuery.h>
#include <Parsers/ASTRoleList.h> #include <Parsers/ASTRoleList.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/formatAST.h> #include <Parsers/formatAST.h>
#include <Parsers/parseQuery.h>
#include <Access/AccessControlManager.h> #include <Access/AccessControlManager.h>
#include <Access/QuotaContext.h> #include <Access/QuotaContext.h>
#include <Columns/ColumnString.h> #include <Columns/ColumnString.h>
@ -28,7 +31,7 @@ BlockInputStreamPtr InterpreterShowCreateAccessEntityQuery::executeImpl()
const auto & show_query = query_ptr->as<ASTShowCreateAccessEntityQuery &>(); const auto & show_query = query_ptr->as<ASTShowCreateAccessEntityQuery &>();
/// Build a create query. /// Build a create query.
ASTPtr create_query = getCreateQuotaQuery(show_query); ASTPtr create_query = getCreateQuery(show_query);
/// Build the result column. /// Build the result column.
std::stringstream create_query_ss; std::stringstream create_query_ss;
@ -49,6 +52,18 @@ BlockInputStreamPtr InterpreterShowCreateAccessEntityQuery::executeImpl()
} }
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuery(const ASTShowCreateAccessEntityQuery & show_query) const
{
using Kind = ASTShowCreateAccessEntityQuery::Kind;
switch (show_query.kind)
{
case Kind::QUOTA: return getCreateQuotaQuery(show_query);
case Kind::ROW_POLICY: return getCreateRowPolicyQuery(show_query);
}
__builtin_unreachable();
}
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const
{ {
auto & access_control = context.getAccessControlManager(); auto & access_control = context.getAccessControlManager();
@ -86,4 +101,38 @@ ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShow
return create_query; return create_query;
} }
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateRowPolicyQuery(const ASTShowCreateAccessEntityQuery & show_query) const
{
auto & access_control = context.getAccessControlManager();
RowPolicyPtr policy = access_control.read<RowPolicy>(show_query.row_policy_name.getFullName(context));
auto create_query = std::make_shared<ASTCreateRowPolicyQuery>();
create_query->name_parts = RowPolicy::FullNameParts{policy->getDatabase(), policy->getTableName(), policy->getName()};
if (policy->isRestrictive())
create_query->is_restrictive = policy->isRestrictive();
for (auto index : ext::range_with_static_cast<RowPolicy::ConditionIndex>(RowPolicy::MAX_CONDITION_INDEX))
{
const auto & condition = policy->conditions[index];
if (!condition.empty())
{
ParserExpression parser;
ASTPtr expr = parseQuery(parser, condition, 0);
create_query->conditions.push_back(std::pair{index, expr});
}
}
if (!policy->roles.empty() || policy->all_roles)
{
auto create_query_roles = std::make_shared<ASTRoleList>();
create_query_roles->roles = policy->roles;
create_query_roles->all_roles = policy->all_roles;
create_query_roles->except_roles = policy->except_roles;
create_query->roles = std::move(create_query_roles);
}
return create_query;
}
} }

View File

@ -28,7 +28,9 @@ private:
const Context & context; const Context & context;
BlockInputStreamPtr executeImpl(); BlockInputStreamPtr executeImpl();
ASTPtr getCreateQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
ASTPtr getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const; ASTPtr getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
ASTPtr getCreateRowPolicyQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
}; };

View File

@ -0,0 +1,68 @@
#include <Interpreters/InterpreterShowRowPoliciesQuery.h>
#include <Parsers/ASTShowRowPoliciesQuery.h>
#include <Parsers/formatAST.h>
#include <Interpreters/executeQuery.h>
#include <Common/quoteString.h>
#include <Interpreters/Context.h>
#include <ext/range.h>
namespace DB
{
InterpreterShowRowPoliciesQuery::InterpreterShowRowPoliciesQuery(const ASTPtr & query_ptr_, Context & context_)
: query_ptr(query_ptr_), context(context_)
{
}
BlockIO InterpreterShowRowPoliciesQuery::execute()
{
return executeQuery(getRewrittenQuery(), context, true);
}
String InterpreterShowRowPoliciesQuery::getRewrittenQuery() const
{
const auto & query = query_ptr->as<ASTShowRowPoliciesQuery &>();
const String & table_name = query.table_name;
String database;
if (!table_name.empty())
{
database = query.database;
if (database.empty())
database = context.getCurrentDatabase();
}
String filter;
if (query.current)
{
if (table_name.empty())
filter = "has(currentRowPolicyIDs(), id)";
else
filter = "has(currentRowPolicyIDs(" + quoteString(database) + ", " + quoteString(table_name) + "), id)";
}
else
{
if (!table_name.empty())
filter = "database = " + quoteString(database) + " AND table = " + quoteString(table_name);
}
String expr = table_name.empty() ? "full_name" : "name";
return "SELECT " + expr + " AS " + backQuote(getResultDescription()) + " from system.row_policies"
+ (filter.empty() ? "" : " WHERE " + filter) + " ORDER BY " + expr;
}
String InterpreterShowRowPoliciesQuery::getResultDescription() const
{
std::stringstream ss;
formatAST(*query_ptr, ss, false, true);
String desc = ss.str();
String prefix = "SHOW ";
if (startsWith(desc, prefix))
desc = desc.substr(prefix.length()); /// `desc` always starts with "SHOW ", so we can trim this prefix.
return desc;
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <Interpreters/IInterpreter.h>
#include <Parsers/IAST_fwd.h>
namespace DB
{
class Context;
class InterpreterShowRowPoliciesQuery : public IInterpreter
{
public:
InterpreterShowRowPoliciesQuery(const ASTPtr & query_ptr_, Context & context_);
BlockIO execute() override;
private:
String getRewrittenQuery() const;
String getResultDescription() const;
ASTPtr query_ptr;
Context & context;
};
}

View File

@ -5,6 +5,7 @@
#include <IO/ReadHelpers.h> #include <IO/ReadHelpers.h>
#include <Interpreters/Users.h> #include <Interpreters/Users.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
#include <Poco/MD5Engine.h>
namespace DB namespace DB
@ -102,36 +103,10 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A
} }
} }
/// Read properties per "database.table"
/// Only tables are expected to have properties, so that all the keys inside "database" are table names.
const auto config_databases = config_elem + ".databases";
if (config.has(config_databases))
{
Poco::Util::AbstractConfiguration::Keys database_names;
config.keys(config_databases, database_names);
/// Read tables within databases
for (const auto & database : database_names)
{
const auto config_database = config_databases + "." + database;
Poco::Util::AbstractConfiguration::Keys table_names;
config.keys(config_database, table_names);
/// Read table properties
for (const auto & table : table_names)
{
const auto config_filter = config_database + "." + table + ".filter";
if (config.has(config_filter))
{
const auto filter_query = config.getString(config_filter);
table_props[database][table]["filter"] = filter_query;
}
}
}
}
if (config.has(config_elem + ".allow_quota_management")) if (config.has(config_elem + ".allow_quota_management"))
is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management"); is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management");
if (config.has(config_elem + ".allow_row_policy_management"))
is_row_policy_management_allowed = config.getBool(config_elem + ".allow_row_policy_management");
} }
} }

View File

@ -1,12 +1,13 @@
#pragma once #pragma once
#include <Core/Types.h> #include <Core/Types.h>
#include <Core/UUID.h>
#include <Access/Authentication.h> #include <Access/Authentication.h>
#include <Access/AllowedClientHosts.h> #include <Access/AllowedClientHosts.h>
#include <memory> #include <memory>
#include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector>
namespace Poco namespace Poco
@ -41,13 +42,8 @@ struct User
using DictionarySet = std::unordered_set<std::string>; using DictionarySet = std::unordered_set<std::string>;
std::optional<DictionarySet> dictionaries; std::optional<DictionarySet> dictionaries;
/// Table properties.
using PropertyMap = std::unordered_map<std::string /* name */, std::string /* value */>;
using TableMap = std::unordered_map<std::string /* table */, PropertyMap /* properties */>;
using DatabaseMap = std::unordered_map<std::string /* database */, TableMap /* tables */>;
DatabaseMap table_props;
bool is_quota_management_allowed = false; bool is_quota_management_allowed = false;
bool is_row_policy_management_allowed = false;
User(const String & name_, const String & config_elem, const Poco::Util::AbstractConfiguration & config); User(const String & name_, const String & config_elem, const Poco::Util::AbstractConfiguration & config);
}; };

View File

@ -0,0 +1,164 @@
#include <Parsers/ASTCreateRowPolicyQuery.h>
#include <Parsers/ASTRoleList.h>
#include <Parsers/formatAST.h>
#include <Common/quoteString.h>
#include <boost/range/algorithm/transform.hpp>
#include <sstream>
namespace DB
{
namespace
{
using ConditionIndex = RowPolicy::ConditionIndex;
void formatRenameTo(const String & new_policy_name, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " RENAME TO " << (settings.hilite ? IAST::hilite_none : "")
<< backQuote(new_policy_name);
}
void formatIsRestrictive(bool is_restrictive, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " AS " << (is_restrictive ? "RESTRICTIVE" : "PERMISSIVE")
<< (settings.hilite ? IAST::hilite_none : "");
}
void formatConditionalExpression(const ASTPtr & expr, const IAST::FormatSettings & settings)
{
if (!expr)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " NONE" << (settings.hilite ? IAST::hilite_none : "");
return;
}
expr->format(settings);
}
std::vector<std::pair<ConditionIndex, String>>
conditionalExpressionsToStrings(const std::vector<std::pair<ConditionIndex, ASTPtr>> & exprs, const IAST::FormatSettings & settings)
{
std::vector<std::pair<ConditionIndex, String>> result;
std::stringstream ss;
IAST::FormatSettings temp_settings(ss, settings);
boost::range::transform(exprs, std::back_inserter(result), [&](const std::pair<ConditionIndex, ASTPtr> & in)
{
formatConditionalExpression(in.second, temp_settings);
auto out = std::pair{in.first, ss.str()};
ss.str("");
return out;
});
return result;
}
void formatConditions(const char * op, const std::optional<String> & filter, const std::optional<String> & check, bool alter, const IAST::FormatSettings & settings)
{
if (op)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " FOR" << (settings.hilite ? IAST::hilite_none : "");
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << ' ' << op << (settings.hilite ? IAST::hilite_none : "");
}
if (filter)
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " USING " << (settings.hilite ? IAST::hilite_none : "") << *filter;
if (check && (alter || (check != filter)))
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " WITH CHECK " << (settings.hilite ? IAST::hilite_none : "") << *check;
}
void formatMultipleConditions(const std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter, const IAST::FormatSettings & settings)
{
std::optional<String> scond[RowPolicy::MAX_CONDITION_INDEX];
for (const auto & [index, scondition] : conditionalExpressionsToStrings(conditions, settings))
scond[index] = scondition;
if ((scond[RowPolicy::SELECT_FILTER] == scond[RowPolicy::UPDATE_FILTER])
&& (scond[RowPolicy::UPDATE_FILTER] == scond[RowPolicy::DELETE_FILTER])
&& (scond[RowPolicy::INSERT_CHECK] == scond[RowPolicy::UPDATE_CHECK])
&& (scond[RowPolicy::SELECT_FILTER] || scond[RowPolicy::INSERT_CHECK]))
{
formatConditions(nullptr, scond[RowPolicy::SELECT_FILTER], scond[RowPolicy::INSERT_CHECK], alter, settings);
return;
}
bool need_comma = false;
if (scond[RowPolicy::SELECT_FILTER])
{
if (std::exchange(need_comma, true))
settings.ostr << ',';
formatConditions("SELECT", scond[RowPolicy::SELECT_FILTER], {}, alter, settings);
}
if (scond[RowPolicy::INSERT_CHECK])
{
if (std::exchange(need_comma, true))
settings.ostr << ',';
formatConditions("INSERT", {}, scond[RowPolicy::INSERT_CHECK], alter, settings);
}
if (scond[RowPolicy::UPDATE_FILTER] || scond[RowPolicy::UPDATE_CHECK])
{
if (std::exchange(need_comma, true))
settings.ostr << ',';
formatConditions("UPDATE", scond[RowPolicy::UPDATE_FILTER], scond[RowPolicy::UPDATE_CHECK], alter, settings);
}
if (scond[RowPolicy::DELETE_FILTER])
{
if (std::exchange(need_comma, true))
settings.ostr << ',';
formatConditions("DELETE", scond[RowPolicy::DELETE_FILTER], {}, alter, settings);
}
}
void formatRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : "");
roles.format(settings);
}
}
String ASTCreateRowPolicyQuery::getID(char) const
{
return "CREATE POLICY or ALTER POLICY query";
}
ASTPtr ASTCreateRowPolicyQuery::clone() const
{
return std::make_shared<ASTCreateRowPolicyQuery>(*this);
}
void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << (alter ? "ALTER POLICY" : "CREATE POLICY")
<< (settings.hilite ? hilite_none : "");
if (if_exists)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " IF EXISTS" << (settings.hilite ? hilite_none : "");
else if (if_not_exists)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " IF NOT EXISTS" << (settings.hilite ? hilite_none : "");
else if (or_replace)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " OR REPLACE" << (settings.hilite ? hilite_none : "");
const String & database = name_parts.database;
const String & table_name = name_parts.table_name;
const String & policy_name = name_parts.policy_name;
settings.ostr << " " << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".") << table_name;
if (!new_policy_name.empty())
formatRenameTo(new_policy_name, settings);
if (is_restrictive)
formatIsRestrictive(*is_restrictive, settings);
formatMultipleConditions(conditions, alter, settings);
if (roles)
formatRoles(*roles, settings);
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#include <Parsers/IAST.h>
#include <Access/RowPolicy.h>
#include <utility>
#include <vector>
namespace DB
{
class ASTRoleList;
/** CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] name ON [database.]table
* [AS {PERMISSIVE | RESTRICTIVE}]
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
* [USING condition]
* [WITH CHECK condition] [,...]
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
*
* ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table
* [RENAME TO new_name]
* [AS {PERMISSIVE | RESTRICTIVE}]
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
* [USING {condition | NONE}]
* [WITH CHECK {condition | NONE}] [,...]
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
*/
class ASTCreateRowPolicyQuery : public IAST
{
public:
bool alter = false;
bool if_exists = false;
bool if_not_exists = false;
bool or_replace = false;
RowPolicy::FullNameParts name_parts;
String new_policy_name;
std::optional<bool> is_restrictive;
using ConditionIndex = RowPolicy::ConditionIndex;
std::vector<std::pair<ConditionIndex, ASTPtr>> conditions;
std::shared_ptr<ASTRoleList> roles;
String getID(char) const override;
ASTPtr clone() const override;
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
};
}

View File

@ -13,6 +13,7 @@ namespace
switch (kind) switch (kind)
{ {
case Kind::QUOTA: return "QUOTA"; case Kind::QUOTA: return "QUOTA";
case Kind::ROW_POLICY: return "POLICY";
} }
__builtin_unreachable(); __builtin_unreachable();
} }
@ -44,13 +45,32 @@ void ASTDropAccessEntityQuery::formatImpl(const FormatSettings & settings, Forma
<< (if_exists ? " IF EXISTS" : "") << (if_exists ? " IF EXISTS" : "")
<< (settings.hilite ? hilite_none : ""); << (settings.hilite ? hilite_none : "");
bool need_comma = false; if (kind == Kind::ROW_POLICY)
for (const auto & name : names)
{ {
if (need_comma) bool need_comma = false;
settings.ostr << ','; for (const auto & row_policy_name : row_policies_names)
need_comma = true; {
settings.ostr << ' ' << backQuoteIfNeed(name); if (need_comma)
settings.ostr << ',';
need_comma = true;
const String & database = row_policy_name.database;
const String & table_name = row_policy_name.table_name;
const String & policy_name = row_policy_name.policy_name;
settings.ostr << ' ' << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".")
<< backQuoteIfNeed(table_name);
}
}
else
{
bool need_comma = false;
for (const auto & name : names)
{
if (need_comma)
settings.ostr << ',';
need_comma = true;
settings.ostr << ' ' << backQuoteIfNeed(name);
}
} }
} }
} }

View File

@ -1,12 +1,14 @@
#pragma once #pragma once
#include <Parsers/IAST.h> #include <Parsers/IAST.h>
#include <Access/RowPolicy.h>
namespace DB namespace DB
{ {
/** DROP QUOTA [IF EXISTS] name [,...] /** DROP QUOTA [IF EXISTS] name [,...]
* DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...]
*/ */
class ASTDropAccessEntityQuery : public IAST class ASTDropAccessEntityQuery : public IAST
{ {
@ -14,11 +16,13 @@ public:
enum class Kind enum class Kind
{ {
QUOTA, QUOTA,
ROW_POLICY,
}; };
const Kind kind; const Kind kind;
const char * const keyword; const char * const keyword;
bool if_exists = false; bool if_exists = false;
Strings names; Strings names;
std::vector<RowPolicy::FullNameParts> row_policies_names;
ASTDropAccessEntityQuery(Kind kind_); ASTDropAccessEntityQuery(Kind kind_);
String getID(char) const override; String getID(char) const override;

View File

@ -13,6 +13,7 @@ namespace
switch (kind) switch (kind)
{ {
case Kind::QUOTA: return "QUOTA"; case Kind::QUOTA: return "QUOTA";
case Kind::ROW_POLICY: return "POLICY";
} }
__builtin_unreachable(); __builtin_unreachable();
} }
@ -43,7 +44,16 @@ void ASTShowCreateAccessEntityQuery::formatQueryImpl(const FormatSettings & sett
<< "SHOW CREATE " << keyword << "SHOW CREATE " << keyword
<< (settings.hilite ? hilite_none : ""); << (settings.hilite ? hilite_none : "");
if (current_quota) if (kind == Kind::ROW_POLICY)
{
const String & database = row_policy_name.database;
const String & table_name = row_policy_name.table_name;
const String & policy_name = row_policy_name.policy_name;
settings.ostr << ' ' << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".")
<< backQuoteIfNeed(table_name);
}
else if ((kind == Kind::QUOTA) && current_quota)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : ""); settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
else else
settings.ostr << " " << backQuoteIfNeed(name); settings.ostr << " " << backQuoteIfNeed(name);

View File

@ -1,11 +1,13 @@
#pragma once #pragma once
#include <Parsers/ASTQueryWithOutput.h> #include <Parsers/ASTQueryWithOutput.h>
#include <Access/RowPolicy.h>
namespace DB namespace DB
{ {
/** SHOW CREATE QUOTA [name | CURRENT] /** SHOW CREATE QUOTA [name | CURRENT]
* SHOW CREATE [ROW] POLICY name ON [database.]table
*/ */
class ASTShowCreateAccessEntityQuery : public ASTQueryWithOutput class ASTShowCreateAccessEntityQuery : public ASTQueryWithOutput
{ {
@ -13,11 +15,13 @@ public:
enum class Kind enum class Kind
{ {
QUOTA, QUOTA,
ROW_POLICY,
}; };
const Kind kind; const Kind kind;
const char * const keyword; const char * const keyword;
String name; String name;
bool current_quota = false; bool current_quota = false;
RowPolicy::FullNameParts row_policy_name;
ASTShowCreateAccessEntityQuery(Kind kind_); ASTShowCreateAccessEntityQuery(Kind kind_);
String getID(char) const override; String getID(char) const override;

View File

@ -0,0 +1,22 @@
#include <Parsers/ASTShowRowPoliciesQuery.h>
#include <Common/quoteString.h>
namespace DB
{
void ASTShowRowPoliciesQuery::formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW POLICIES" << (settings.hilite ? hilite_none : "");
if (current)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
if (!table_name.empty())
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ON " << (settings.hilite ? hilite_none : "");
if (!database.empty())
settings.ostr << backQuoteIfNeed(database) << ".";
settings.ostr << backQuoteIfNeed(table_name);
}
}
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <Parsers/ASTQueryWithOutput.h>
namespace DB
{
/// SHOW [ROW] POLICIES [CURRENT] [ON [database.]table]
class ASTShowRowPoliciesQuery : public ASTQueryWithOutput
{
public:
bool current = false;
String database;
String table_name;
String getID(char) const override { return "SHOW POLICIES query"; }
ASTPtr clone() const override { return std::make_shared<ASTShowRowPoliciesQuery>(*this); }
protected:
void formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
};
}

View File

@ -0,0 +1,261 @@
#include <Parsers/ParserCreateRowPolicyQuery.h>
#include <Parsers/ASTCreateRowPolicyQuery.h>
#include <Access/RowPolicy.h>
#include <Parsers/ParserRoleList.h>
#include <Parsers/ASTRoleList.h>
#include <Parsers/parseIdentifierOrStringLiteral.h>
#include <Parsers/parseDatabaseAndTableName.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/ASTLiteral.h>
namespace DB
{
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
}
namespace
{
using ConditionIndex = RowPolicy::ConditionIndex;
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_policy_name, bool alter)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (!new_policy_name.empty() || !alter)
return false;
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
return false;
return parseIdentifierOrStringLiteral(pos, expected, new_policy_name);
});
}
bool parseIsRestrictive(IParserBase::Pos & pos, Expected & expected, std::optional<bool> & is_restrictive)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (is_restrictive)
return false;
if (!ParserKeyword{"AS"}.ignore(pos, expected))
return false;
if (ParserKeyword{"RESTRICTIVE"}.ignore(pos, expected))
is_restrictive = true;
else if (ParserKeyword{"PERMISSIVE"}.ignore(pos, expected))
is_restrictive = false;
else
return false;
return true;
});
}
bool parseConditionalExpression(IParserBase::Pos & pos, Expected & expected, std::optional<ASTPtr> & expr)
{
if (ParserKeyword("NONE").ignore(pos, expected))
{
expr = nullptr;
return true;
}
ParserExpression parser;
ASTPtr x;
if (parser.parse(pos, x, expected))
{
expr = x;
return true;
}
expr.reset();
return false;
}
bool parseConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
{
return IParserBase::wrapParseImpl(pos, [&]
{
static constexpr char select_op[] = "SELECT";
static constexpr char insert_op[] = "INSERT";
static constexpr char update_op[] = "UPDATE";
static constexpr char delete_op[] = "DELETE";
std::vector<const char *> ops;
bool keyword_for = false;
if (ParserKeyword{"FOR"}.ignore(pos, expected))
{
keyword_for = true;
do
{
if (ParserKeyword{"SELECT"}.ignore(pos, expected))
ops.push_back(select_op);
else if (ParserKeyword{"INSERT"}.ignore(pos, expected))
ops.push_back(insert_op);
else if (ParserKeyword{"UPDATE"}.ignore(pos, expected))
ops.push_back(update_op);
else if (ParserKeyword{"DELETE"}.ignore(pos, expected))
ops.push_back(delete_op);
else if (ParserKeyword{"ALL"}.ignore(pos, expected))
{
}
else
return false;
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
}
if (ops.empty())
{
ops.push_back(select_op);
ops.push_back(insert_op);
ops.push_back(update_op);
ops.push_back(delete_op);
}
std::optional<ASTPtr> filter;
std::optional<ASTPtr> check;
bool keyword_using = false, keyword_with_check = false;
if (ParserKeyword{"USING"}.ignore(pos, expected))
{
keyword_using = true;
if (!parseConditionalExpression(pos, expected, filter))
return false;
}
if (ParserKeyword{"WITH CHECK"}.ignore(pos, expected))
{
keyword_with_check = true;
if (!parseConditionalExpression(pos, expected, check))
return false;
}
if (!keyword_for && !keyword_using && !keyword_with_check)
return false;
if (filter && !check && !alter)
check = filter;
auto set_condition = [&](ConditionIndex index, const ASTPtr & condition)
{
auto it = std::find_if(conditions.begin(), conditions.end(), [index](const std::pair<ConditionIndex, ASTPtr> & element)
{
return element.first == index;
});
if (it == conditions.end())
it = conditions.insert(conditions.end(), std::pair<ConditionIndex, ASTPtr>{index, nullptr});
it->second = condition;
};
for (const auto & op : ops)
{
if ((op == select_op) && filter)
set_condition(RowPolicy::SELECT_FILTER, *filter);
else if ((op == insert_op) && check)
set_condition(RowPolicy::INSERT_CHECK, *check);
else if (op == update_op)
{
if (filter)
set_condition(RowPolicy::UPDATE_FILTER, *filter);
if (check)
set_condition(RowPolicy::UPDATE_CHECK, *check);
}
else if ((op == delete_op) && filter)
set_condition(RowPolicy::DELETE_FILTER, *filter);
else
__builtin_unreachable();
}
return true;
});
}
bool parseMultipleConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
{
return IParserBase::wrapParseImpl(pos, [&]
{
do
{
if (!parseConditions(pos, expected, conditions, alter))
return false;
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
return true;
});
}
bool parseRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
{
return IParserBase::wrapParseImpl(pos, [&]
{
ASTPtr node;
if (roles || !ParserKeyword{"TO"}.ignore(pos, expected) || !ParserRoleList{}.parse(pos, node, expected))
return false;
roles = std::static_pointer_cast<ASTRoleList>(node);
return true;
});
}
}
bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
bool alter;
if (ParserKeyword{"CREATE POLICY"}.ignore(pos, expected) || ParserKeyword{"CREATE ROW POLICY"}.ignore(pos, expected))
alter = false;
else if (ParserKeyword{"ALTER POLICY"}.ignore(pos, expected) || ParserKeyword{"ALTER ROW POLICY"}.ignore(pos, expected))
alter = true;
else
return false;
bool if_exists = false;
bool if_not_exists = false;
bool or_replace = false;
if (alter)
{
if (ParserKeyword{"IF EXISTS"}.ignore(pos, expected))
if_exists = true;
}
else
{
if (ParserKeyword{"IF NOT EXISTS"}.ignore(pos, expected))
if_not_exists = true;
else if (ParserKeyword{"OR REPLACE"}.ignore(pos, expected))
or_replace = true;
}
RowPolicy::FullNameParts name_parts;
String & database = name_parts.database;
String & table_name = name_parts.table_name;
String & policy_name = name_parts.policy_name;
if (!parseIdentifierOrStringLiteral(pos, expected, policy_name) || !ParserKeyword{"ON"}.ignore(pos, expected)
|| !parseDatabaseAndTableName(pos, expected, database, table_name))
return false;
String new_policy_name;
std::optional<bool> is_restrictive;
std::vector<std::pair<ConditionIndex, ASTPtr>> conditions;
std::shared_ptr<ASTRoleList> roles;
while (parseRenameTo(pos, expected, new_policy_name, alter) || parseIsRestrictive(pos, expected, is_restrictive)
|| parseMultipleConditions(pos, expected, conditions, alter) || parseRoles(pos, expected, roles))
;
auto query = std::make_shared<ASTCreateRowPolicyQuery>();
node = query;
query->alter = alter;
query->if_exists = if_exists;
query->if_not_exists = if_not_exists;
query->or_replace = or_replace;
query->name_parts = std::move(name_parts);
query->new_policy_name = std::move(new_policy_name);
query->is_restrictive = is_restrictive;
query->conditions = std::move(conditions);
query->roles = std::move(roles);
return true;
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <Parsers/IParserBase.h>
namespace DB
{
/** Parses queries like
* CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] name ON [database.]table
* [AS {PERMISSIVE | RESTRICTIVE}]
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
* [USING condition]
* [WITH CHECK condition] [,...]
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
*
* ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table
* [RENAME TO new_name]
* [AS {PERMISSIVE | RESTRICTIVE}]
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
* [USING {condition | NONE}]
* [WITH CHECK {condition | NONE}] [,...]
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
*/
class ParserCreateRowPolicyQuery : public IParserBase
{
protected:
const char * getName() const override { return "CREATE ROW POLICY or ALTER ROW POLICY query"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
};
}

View File

@ -2,11 +2,30 @@
#include <Parsers/ASTDropAccessEntityQuery.h> #include <Parsers/ASTDropAccessEntityQuery.h>
#include <Parsers/CommonParsers.h> #include <Parsers/CommonParsers.h>
#include <Parsers/parseIdentifierOrStringLiteral.h> #include <Parsers/parseIdentifierOrStringLiteral.h>
#include <Parsers/parseDatabaseAndTableName.h>
#include <Access/Quota.h> #include <Access/Quota.h>
namespace DB namespace DB
{ {
namespace
{
bool parseNames(IParserBase::Pos & pos, Expected & expected, Strings & names)
{
do
{
String name;
if (!parseIdentifierOrStringLiteral(pos, expected, name))
return false;
names.push_back(std::move(name));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
return true;
}
}
bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{ {
if (!ParserKeyword{"DROP"}.ignore(pos, expected)) if (!ParserKeyword{"DROP"}.ignore(pos, expected))
@ -16,6 +35,8 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
Kind kind; Kind kind;
if (ParserKeyword{"QUOTA"}.ignore(pos, expected)) if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
kind = Kind::QUOTA; kind = Kind::QUOTA;
else if (ParserKeyword{"POLICY"}.ignore(pos, expected) || ParserKeyword{"ROW POLICY"}.ignore(pos, expected))
kind = Kind::ROW_POLICY;
else else
return false; return false;
@ -24,21 +45,35 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
if_exists = true; if_exists = true;
Strings names; Strings names;
do std::vector<RowPolicy::FullNameParts> row_policies_names;
{
String name;
if (!parseIdentifierOrStringLiteral(pos, expected, name))
return false;
names.push_back(std::move(name)); if (kind == Kind::ROW_POLICY)
{
do
{
Strings policy_names;
if (!parseNames(pos, expected, policy_names))
return false;
String database, table_name;
if (!ParserKeyword{"ON"}.ignore(pos, expected) || !parseDatabaseAndTableName(pos, expected, database, table_name))
return false;
for (const String & policy_name : policy_names)
row_policies_names.push_back({database, table_name, policy_name});
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
}
else
{
if (!parseNames(pos, expected, names))
return false;
} }
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
auto query = std::make_shared<ASTDropAccessEntityQuery>(kind); auto query = std::make_shared<ASTDropAccessEntityQuery>(kind);
node = query; node = query;
query->if_exists = if_exists; query->if_exists = if_exists;
query->names = std::move(names); query->names = std::move(names);
query->row_policies_names = std::move(row_policies_names);
return true; return true;
} }

View File

@ -10,6 +10,7 @@
#include <Parsers/ParserAlterQuery.h> #include <Parsers/ParserAlterQuery.h>
#include <Parsers/ParserSystemQuery.h> #include <Parsers/ParserSystemQuery.h>
#include <Parsers/ParserCreateQuotaQuery.h> #include <Parsers/ParserCreateQuotaQuery.h>
#include <Parsers/ParserCreateRowPolicyQuery.h>
#include <Parsers/ParserDropAccessEntityQuery.h> #include <Parsers/ParserDropAccessEntityQuery.h>
@ -25,6 +26,7 @@ bool ParserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ParserSetQuery set_p; ParserSetQuery set_p;
ParserSystemQuery system_p; ParserSystemQuery system_p;
ParserCreateQuotaQuery create_quota_p; ParserCreateQuotaQuery create_quota_p;
ParserCreateRowPolicyQuery create_row_policy_p;
ParserDropAccessEntityQuery drop_access_entity_p; ParserDropAccessEntityQuery drop_access_entity_p;
bool res = query_with_output_p.parse(pos, node, expected) bool res = query_with_output_p.parse(pos, node, expected)
@ -33,6 +35,7 @@ bool ParserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|| set_p.parse(pos, node, expected) || set_p.parse(pos, node, expected)
|| system_p.parse(pos, node, expected) || system_p.parse(pos, node, expected)
|| create_quota_p.parse(pos, node, expected) || create_quota_p.parse(pos, node, expected)
|| create_row_policy_p.parse(pos, node, expected)
|| drop_access_entity_p.parse(pos, node, expected); || drop_access_entity_p.parse(pos, node, expected);
return res; return res;

View File

@ -16,6 +16,7 @@
#include <Parsers/ASTExplainQuery.h> #include <Parsers/ASTExplainQuery.h>
#include <Parsers/ParserShowCreateAccessEntityQuery.h> #include <Parsers/ParserShowCreateAccessEntityQuery.h>
#include <Parsers/ParserShowQuotasQuery.h> #include <Parsers/ParserShowQuotasQuery.h>
#include <Parsers/ParserShowRowPoliciesQuery.h>
namespace DB namespace DB
@ -38,6 +39,7 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
ParserWatchQuery watch_p; ParserWatchQuery watch_p;
ParserShowCreateAccessEntityQuery show_create_access_entity_p; ParserShowCreateAccessEntityQuery show_create_access_entity_p;
ParserShowQuotasQuery show_quotas_p; ParserShowQuotasQuery show_quotas_p;
ParserShowRowPoliciesQuery show_row_policies_p;
ASTPtr query; ASTPtr query;
@ -66,7 +68,8 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|| kill_query_p.parse(pos, query, expected) || kill_query_p.parse(pos, query, expected)
|| optimize_p.parse(pos, query, expected) || optimize_p.parse(pos, query, expected)
|| watch_p.parse(pos, query, expected) || watch_p.parse(pos, query, expected)
|| show_quotas_p.parse(pos, query, expected); || show_quotas_p.parse(pos, query, expected)
|| show_row_policies_p.parse(pos, query, expected);
if (!parsed) if (!parsed)
return false; return false;

View File

@ -2,6 +2,8 @@
#include <Parsers/ASTShowCreateAccessEntityQuery.h> #include <Parsers/ASTShowCreateAccessEntityQuery.h>
#include <Parsers/CommonParsers.h> #include <Parsers/CommonParsers.h>
#include <Parsers/parseIdentifierOrStringLiteral.h> #include <Parsers/parseIdentifierOrStringLiteral.h>
#include <Parsers/parseDatabaseAndTableName.h>
#include <assert.h>
namespace DB namespace DB
@ -15,25 +17,41 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
Kind kind; Kind kind;
if (ParserKeyword{"QUOTA"}.ignore(pos, expected)) if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
kind = Kind::QUOTA; kind = Kind::QUOTA;
else if (ParserKeyword{"POLICY"}.ignore(pos, expected) || ParserKeyword{"ROW POLICY"}.ignore(pos, expected))
kind = Kind::ROW_POLICY;
else else
return false; return false;
String name; String name;
bool current_quota = false; bool current_quota = false;
RowPolicy::FullNameParts row_policy_name;
if ((kind == Kind::QUOTA) && ParserKeyword{"CURRENT"}.ignore(pos, expected)) if (kind == Kind::ROW_POLICY)
{ {
/// SHOW CREATE QUOTA CURRENT String & database = row_policy_name.database;
current_quota = true; String & table_name = row_policy_name.table_name;
} String & policy_name = row_policy_name.policy_name;
else if (parseIdentifierOrStringLiteral(pos, expected, name)) if (!parseIdentifierOrStringLiteral(pos, expected, policy_name) || !ParserKeyword{"ON"}.ignore(pos, expected)
{ || !parseDatabaseAndTableName(pos, expected, database, table_name))
/// SHOW CREATE QUOTA name return false;
} }
else else
{ {
/// SHOW CREATE QUOTA assert(kind == Kind::QUOTA);
current_quota = true; if (ParserKeyword{"CURRENT"}.ignore(pos, expected))
{
/// SHOW CREATE QUOTA CURRENT
current_quota = true;
}
else if (parseIdentifierOrStringLiteral(pos, expected, name))
{
/// SHOW CREATE QUOTA name
}
else
{
/// SHOW CREATE QUOTA
current_quota = true;
}
} }
auto query = std::make_shared<ASTShowCreateAccessEntityQuery>(kind); auto query = std::make_shared<ASTShowCreateAccessEntityQuery>(kind);
@ -41,6 +59,7 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
query->name = std::move(name); query->name = std::move(name);
query->current_quota = current_quota; query->current_quota = current_quota;
query->row_policy_name = std::move(row_policy_name);
return true; return true;
} }

View File

@ -0,0 +1,40 @@
#include <Parsers/ParserShowRowPoliciesQuery.h>
#include <Parsers/ASTShowRowPoliciesQuery.h>
#include <Parsers/CommonParsers.h>
#include <Parsers/parseDatabaseAndTableName.h>
namespace DB
{
namespace
{
bool parseONDatabaseAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, String & table_name)
{
return IParserBase::wrapParseImpl(pos, [&]
{
database.clear();
table_name.clear();
return ParserKeyword{"ON"}.ignore(pos, expected) && parseDatabaseAndTableName(pos, expected, database, table_name);
});
}
}
bool ParserShowRowPoliciesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
if (!ParserKeyword{"SHOW POLICIES"}.ignore(pos, expected) && !ParserKeyword{"SHOW ROW POLICIES"}.ignore(pos, expected))
return false;
bool current = ParserKeyword{"CURRENT"}.ignore(pos, expected);
String database, table_name;
parseONDatabaseAndTableName(pos, expected, database, table_name);
auto query = std::make_shared<ASTShowRowPoliciesQuery>();
query->current = current;
query->database = std::move(database);
query->table_name = std::move(table_name);
node = query;
return true;
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <Parsers/IParserBase.h>
namespace DB
{
/** Parses queries like
* SHOW [ROW] POLICIES [CURRENT] [ON [database.]table]
*/
class ParserShowRowPoliciesQuery : public IParserBase
{
protected:
const char * getName() const override { return "SHOW POLICIES query"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
};
}

View File

@ -27,7 +27,7 @@ public:
/** In some usecase (hello Kafka) we need to read a lot of tiny streams in exactly the same format. /** In some usecase (hello Kafka) we need to read a lot of tiny streams in exactly the same format.
* The recreating of parser for each small stream takes too long, so we introduce a method * The recreating of parser for each small stream takes too long, so we introduce a method
* resetParser() which allow to reset the state of parser to continure reading of * resetParser() which allow to reset the state of parser to continue reading of
* source stream w/o recreating that. * source stream w/o recreating that.
* That should be called after current buffer was fully read. * That should be called after current buffer was fully read.
*/ */

View File

@ -429,21 +429,30 @@ IProcessor::Status SortingAggregatedTransform::prepare()
continue; continue;
} }
all_finished = false; //all_finished = false;
in->setNeeded(); in->setNeeded();
if (!in->hasData()) if (!in->hasData())
{ {
need_data = true; need_data = true;
all_finished = false;
continue; continue;
} }
auto chunk = in->pull(); auto chunk = in->pull();
/// If chunk was pulled, then we need data from this port.
need_data = true;
addChunk(std::move(chunk), input_num); addChunk(std::move(chunk), input_num);
if (in->isFinished())
{
is_input_finished[input_num] = true;
}
else
{
/// If chunk was pulled, then we need data from this port.
need_data = true;
all_finished = false;
}
} }
if (pushed_to_output) if (pushed_to_output)

View File

@ -638,11 +638,22 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new
else else
{ {
auto new_ttl_entry = create_ttl_entry(ttl_element.children[0]); auto new_ttl_entry = create_ttl_entry(ttl_element.children[0]);
new_ttl_entry.entry_ast = ttl_element_ptr;
new_ttl_entry.destination_type = ttl_element.destination_type;
new_ttl_entry.destination_name = ttl_element.destination_name;
if (!new_ttl_entry.getDestination(getStoragePolicy()))
{
String message;
if (new_ttl_entry.destination_type == PartDestinationType::DISK)
message = "No such disk " + backQuote(new_ttl_entry.destination_name) + " for given storage policy.";
else
message = "No such volume " + backQuote(new_ttl_entry.destination_name) + " for given storage policy.";
throw Exception(message, ErrorCodes::BAD_TTL_EXPRESSION);
}
if (!only_check) if (!only_check)
{ {
new_ttl_entry.entry_ast = ttl_element_ptr;
new_ttl_entry.destination_type = ttl_element.destination_type;
new_ttl_entry.destination_name = ttl_element.destination_name;
move_ttl_entries.emplace_back(std::move(new_ttl_entry)); move_ttl_entries.emplace_back(std::move(new_ttl_entry));
} }
} }

View File

@ -140,35 +140,36 @@ void registerStorageJoin(StorageFactory & factory)
{ {
const String strictness_str = Poco::toLower(*opt_strictness_id); const String strictness_str = Poco::toLower(*opt_strictness_id);
if (strictness_str == "any" || strictness_str == "\'any\'") if (strictness_str == "any")
{ {
if (old_any_join) if (old_any_join)
strictness = ASTTableJoin::Strictness::RightAny; strictness = ASTTableJoin::Strictness::RightAny;
else else
strictness = ASTTableJoin::Strictness::Any; strictness = ASTTableJoin::Strictness::Any;
} }
else if (strictness_str == "all" || strictness_str == "\'all\'") else if (strictness_str == "all")
strictness = ASTTableJoin::Strictness::All; strictness = ASTTableJoin::Strictness::All;
else if (strictness_str == "semi" || strictness_str == "\'semi\'") else if (strictness_str == "semi")
strictness = ASTTableJoin::Strictness::Semi; strictness = ASTTableJoin::Strictness::Semi;
else if (strictness_str == "anti" || strictness_str == "\'anti\'") else if (strictness_str == "anti")
strictness = ASTTableJoin::Strictness::Anti; strictness = ASTTableJoin::Strictness::Anti;
} }
if (strictness == ASTTableJoin::Strictness::Unspecified) if (strictness == ASTTableJoin::Strictness::Unspecified)
throw Exception("First parameter of storage Join must be ANY or ALL or SEMI or ANTI.", ErrorCodes::BAD_ARGUMENTS); throw Exception("First parameter of storage Join must be ANY or ALL or SEMI or ANTI (without quotes).",
ErrorCodes::BAD_ARGUMENTS);
if (auto opt_kind_id = tryGetIdentifierName(engine_args[1])) if (auto opt_kind_id = tryGetIdentifierName(engine_args[1]))
{ {
const String kind_str = Poco::toLower(*opt_kind_id); const String kind_str = Poco::toLower(*opt_kind_id);
if (kind_str == "left" || kind_str == "\'left\'") if (kind_str == "left")
kind = ASTTableJoin::Kind::Left; kind = ASTTableJoin::Kind::Left;
else if (kind_str == "inner" || kind_str == "\'inner\'") else if (kind_str == "inner")
kind = ASTTableJoin::Kind::Inner; kind = ASTTableJoin::Kind::Inner;
else if (kind_str == "right" || kind_str == "\'right\'") else if (kind_str == "right")
kind = ASTTableJoin::Kind::Right; kind = ASTTableJoin::Kind::Right;
else if (kind_str == "full" || kind_str == "\'full\'") else if (kind_str == "full")
{ {
if (strictness == ASTTableJoin::Strictness::Any) if (strictness == ASTTableJoin::Strictness::Any)
strictness = ASTTableJoin::Strictness::RightAny; strictness = ASTTableJoin::Strictness::RightAny;
@ -177,7 +178,8 @@ void registerStorageJoin(StorageFactory & factory)
} }
if (kind == ASTTableJoin::Kind::Comma) if (kind == ASTTableJoin::Kind::Comma)
throw Exception("Second parameter of storage Join must be LEFT or INNER or RIGHT or FULL.", ErrorCodes::BAD_ARGUMENTS); throw Exception("Second parameter of storage Join must be LEFT or INNER or RIGHT or FULL (without quotes).",
ErrorCodes::BAD_ARGUMENTS);
Names key_names; Names key_names;
key_names.reserve(engine_args.size() - 2); key_names.reserve(engine_args.size() - 2);

View File

@ -196,7 +196,7 @@ BlockInputStreams IStorageURLBase::read(const Names & column_names,
context, context,
max_block_size, max_block_size,
ConnectionTimeouts::getHTTPTimeouts(context), ConnectionTimeouts::getHTTPTimeouts(context),
IStorage::chooseCompressionMethod(request_uri.toString(), compression_method)); IStorage::chooseCompressionMethod(request_uri.getPath(), compression_method));
auto column_defaults = getColumns().getDefaults(); auto column_defaults = getColumns().getDefaults();
if (column_defaults.empty()) if (column_defaults.empty())

View File

@ -0,0 +1,59 @@
#include <Storages/System/StorageSystemRowPolicies.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeUUID.h>
#include <DataTypes/DataTypeDateTime.h>
#include <DataTypes/DataTypeNullable.h>
#include <Interpreters/Context.h>
#include <Access/AccessControlManager.h>
#include <Access/RowPolicy.h>
#include <ext/range.h>
namespace DB
{
NamesAndTypesList StorageSystemRowPolicies::getNamesAndTypes()
{
NamesAndTypesList names_and_types{
{"database", std::make_shared<DataTypeString>()},
{"table", std::make_shared<DataTypeString>()},
{"name", std::make_shared<DataTypeString>()},
{"full_name", std::make_shared<DataTypeString>()},
{"id", std::make_shared<DataTypeUUID>()},
{"source", std::make_shared<DataTypeString>()},
{"restrictive", std::make_shared<DataTypeUInt8>()},
};
for (auto index : ext::range_with_static_cast<RowPolicy::ConditionIndex>(RowPolicy::MAX_CONDITION_INDEX))
names_and_types.push_back({RowPolicy::conditionIndexToColumnName(index), std::make_shared<DataTypeString>()});
return names_and_types;
}
void StorageSystemRowPolicies::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const
{
const auto & access_control = context.getAccessControlManager();
std::vector<UUID> ids = access_control.findAll<RowPolicy>();
for (const auto & id : ids)
{
auto policy = access_control.tryRead<RowPolicy>(id);
if (!policy)
continue;
const auto * storage = access_control.findStorage(id);
size_t i = 0;
res_columns[i++]->insert(policy->getDatabase());
res_columns[i++]->insert(policy->getTableName());
res_columns[i++]->insert(policy->getName());
res_columns[i++]->insert(policy->getFullName());
res_columns[i++]->insert(id);
res_columns[i++]->insert(storage ? storage->getStorageName() : "");
res_columns[i++]->insert(policy->isRestrictive());
for (auto index : ext::range(RowPolicy::MAX_CONDITION_INDEX))
res_columns[i++]->insert(policy->conditions[index]);
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <ext/shared_ptr_helper.h>
#include <Storages/System/IStorageSystemOneBlock.h>
namespace DB
{
class Context;
/// Implements `row_policies` system table, which allows you to get information about row policies.
class StorageSystemRowPolicies : public ext::shared_ptr_helper<StorageSystemRowPolicies>, public IStorageSystemOneBlock<StorageSystemRowPolicies>
{
public:
std::string getName() const override { return "SystemRowPolicies"; }
static NamesAndTypesList getNamesAndTypes();
protected:
friend struct ext::shared_ptr_helper<StorageSystemRowPolicies>;
using IStorageSystemOneBlock::IStorageSystemOneBlock;
void fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const override;
};
}

View File

@ -29,6 +29,7 @@
#include <Storages/System/StorageSystemQuotaUsage.h> #include <Storages/System/StorageSystemQuotaUsage.h>
#include <Storages/System/StorageSystemReplicas.h> #include <Storages/System/StorageSystemReplicas.h>
#include <Storages/System/StorageSystemReplicationQueue.h> #include <Storages/System/StorageSystemReplicationQueue.h>
#include <Storages/System/StorageSystemRowPolicies.h>
#include <Storages/System/StorageSystemSettings.h> #include <Storages/System/StorageSystemSettings.h>
#include <Storages/System/StorageSystemMergeTreeSettings.h> #include <Storages/System/StorageSystemMergeTreeSettings.h>
#include <Storages/System/StorageSystemTableEngines.h> #include <Storages/System/StorageSystemTableEngines.h>
@ -60,6 +61,7 @@ void attachSystemTablesLocal(IDatabase & system_database)
system_database.attachTable("settings", StorageSystemSettings::create("settings")); system_database.attachTable("settings", StorageSystemSettings::create("settings"));
system_database.attachTable("quotas", StorageSystemQuotas::create("quotas")); system_database.attachTable("quotas", StorageSystemQuotas::create("quotas"));
system_database.attachTable("quota_usage", StorageSystemQuotaUsage::create("quota_usage")); system_database.attachTable("quota_usage", StorageSystemQuotaUsage::create("quota_usage"));
system_database.attachTable("row_policies", StorageSystemRowPolicies::create("row_policies"));
system_database.attachTable("merge_tree_settings", SystemMergeTreeSettings::create("merge_tree_settings")); system_database.attachTable("merge_tree_settings", SystemMergeTreeSettings::create("merge_tree_settings"));
system_database.attachTable("build_options", StorageSystemBuildOptions::create("build_options")); system_database.attachTable("build_options", StorageSystemBuildOptions::create("build_options"));
system_database.attachTable("formats", StorageSystemFormats::create("formats")); system_database.attachTable("formats", StorageSystemFormats::create("formats"));

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<yandex>
<users>
<default>
<!-- Allows all rows -->
<databases>
<mydb>
<filtered_table1>
<filter>1</filter>
</filtered_table1>
<filtered_table2>
<filter>1</filter>
</filtered_table2>
<filtered_table3>
<filter>1</filter>
</filtered_table3>
</mydb>
</databases>
</default>
</users>
</yandex>

View File

@ -0,0 +1 @@
../../normal_filters.xml

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<yandex>
<profiles>
<default>
<any_join_distinct_right_table_keys>1</any_join_distinct_right_table_keys>
</default>
</profiles>
<users>
<default>
<password></password>
<networks incl="networks" replace="replace">
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
<allow_row_policy_management>true</allow_row_policy_management>
</default>
</users>
<quotas>
<default>
</default>
</quotas>
</yandex>

View File

@ -0,0 +1,3 @@
<?xml version="1.0"?>
<yandex>
</yandex>

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<yandex>
<users>
<default>
<!-- Allows no rows -->
<databases>
<mydb>
<filtered_table1>
<filter>NULL</filter>
</filtered_table1>
<filtered_table2>
<filter>NULL</filter>
</filtered_table2>
<filtered_table3>
<filter>NULL</filter>
</filtered_table3>
</mydb>
</databases>
</default>
</users>
</yandex>

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<yandex>
<users>
<default>
<!-- For testing the table filters -->
<databases>
<mydb>
<!-- Simple expression filter -->
<filtered_table1>
<filter>a = 1</filter>
</filtered_table1>
<!-- Complex expression filter -->
<filtered_table2>
<filter>a + b &lt; 1 or c - d &gt; 5</filter>
</filtered_table2>
<!-- Filter with ALIAS column -->
<filtered_table3>
<filter>c = 1</filter>
</filtered_table3>
</mydb>
</databases>
</default>
</users>
</yandex>

View File

@ -0,0 +1,209 @@
import pytest
from helpers.cluster import ClickHouseCluster
from helpers.test_tools import assert_eq_with_retry
import os
import re
import time
cluster = ClickHouseCluster(__file__)
instance = cluster.add_instance('instance',
config_dir="configs")
def copy_policy_xml(local_file_name, reload_immediately = True):
script_dir = os.path.dirname(os.path.realpath(__file__))
instance.copy_file_to_container(os.path.join(script_dir, local_file_name), '/etc/clickhouse-server/users.d/row_policy.xml')
if reload_immediately:
instance.query("SYSTEM RELOAD CONFIG")
@pytest.fixture(scope="module", autouse=True)
def started_cluster():
try:
cluster.start()
instance.query('''
CREATE DATABASE mydb;
CREATE TABLE mydb.filtered_table1 (a UInt8, b UInt8) ENGINE MergeTree ORDER BY a;
INSERT INTO mydb.filtered_table1 values (0, 0), (0, 1), (1, 0), (1, 1);
CREATE TABLE mydb.filtered_table2 (a UInt8, b UInt8, c UInt8, d UInt8) ENGINE MergeTree ORDER BY a;
INSERT INTO mydb.filtered_table2 values (0, 0, 0, 0), (1, 2, 3, 4), (4, 3, 2, 1), (0, 0, 6, 0);
CREATE TABLE mydb.filtered_table3 (a UInt8, b UInt8, c UInt16 ALIAS a + b) ENGINE MergeTree ORDER BY a;
INSERT INTO mydb.filtered_table3 values (0, 0), (0, 1), (1, 0), (1, 1);
''')
yield cluster
finally:
cluster.shutdown()
@pytest.fixture(autouse=True)
def reset_policies():
try:
yield
finally:
copy_policy_xml('normal_filters.xml')
instance.query("DROP POLICY IF EXISTS pA, pB ON mydb.filtered_table1")
def test_smoke():
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t1\n1\t0\n"
assert instance.query("SELECT a FROM mydb.filtered_table1") == "1\n1\n"
assert instance.query("SELECT b FROM mydb.filtered_table1") == "0\n1\n"
assert instance.query("SELECT a FROM mydb.filtered_table1 WHERE a = 1") == "1\n1\n"
assert instance.query("SELECT a = 1 FROM mydb.filtered_table1") == "1\n1\n"
assert instance.query("SELECT a FROM mydb.filtered_table3") == "0\n1\n"
assert instance.query("SELECT b FROM mydb.filtered_table3") == "1\n0\n"
assert instance.query("SELECT c FROM mydb.filtered_table3") == "1\n1\n"
assert instance.query("SELECT a + b FROM mydb.filtered_table3") == "1\n1\n"
assert instance.query("SELECT a FROM mydb.filtered_table3 WHERE c = 1") == "0\n1\n"
assert instance.query("SELECT c = 1 FROM mydb.filtered_table3") == "1\n1\n"
assert instance.query("SELECT a + b = 1 FROM mydb.filtered_table3") == "1\n1\n"
def test_join():
assert instance.query("SELECT * FROM mydb.filtered_table1 as t1 ANY LEFT JOIN mydb.filtered_table1 as t2 ON t1.a = t2.b") == "1\t0\t1\t1\n1\t1\t1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table1 as t2 ANY RIGHT JOIN mydb.filtered_table1 as t1 ON t2.b = t1.a") == "1\t1\t1\t0\n"
def test_cannot_trick_row_policy_with_keyword_with():
assert instance.query("WITH 0 AS a SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("WITH 0 AS a SELECT a, b FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("WITH 0 AS a SELECT a FROM mydb.filtered_table1") == "1\n1\n"
assert instance.query("WITH 0 AS a SELECT b FROM mydb.filtered_table1") == "0\n1\n"
def test_prewhere_not_supported():
expected_error = "PREWHERE is not supported if the table is filtered by row-level security"
assert expected_error in instance.query_and_get_error("SELECT * FROM mydb.filtered_table1 PREWHERE 1")
assert expected_error in instance.query_and_get_error("SELECT * FROM mydb.filtered_table2 PREWHERE 1")
assert expected_error in instance.query_and_get_error("SELECT * FROM mydb.filtered_table3 PREWHERE 1")
def test_change_of_users_xml_changes_row_policies():
copy_policy_xml('normal_filters.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t1\n1\t0\n"
copy_policy_xml('all_rows.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n1\t2\t3\t4\n4\t3\t2\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t0\n0\t1\n1\t0\n1\t1\n"
copy_policy_xml('no_rows.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == ""
assert instance.query("SELECT * FROM mydb.filtered_table2") == ""
assert instance.query("SELECT * FROM mydb.filtered_table3") == ""
copy_policy_xml('normal_filters.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t1\n1\t0\n"
copy_policy_xml('no_filters.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n1\t2\t3\t4\n4\t3\t2\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t0\n0\t1\n1\t0\n1\t1\n"
copy_policy_xml('normal_filters.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t1\n1\t0\n"
def test_reload_users_xml_by_timer():
copy_policy_xml('normal_filters.xml')
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n1\t1\n"
assert instance.query("SELECT * FROM mydb.filtered_table2") == "0\t0\t0\t0\n0\t0\t6\t0\n"
assert instance.query("SELECT * FROM mydb.filtered_table3") == "0\t1\n1\t0\n"
time.sleep(1) # The modification time of the 'row_policy.xml' file should be different.
copy_policy_xml('all_rows.xml', False)
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table1", "0\t0\n0\t1\n1\t0\n1\t1")
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table2", "0\t0\t0\t0\n0\t0\t6\t0\n1\t2\t3\t4\n4\t3\t2\t1")
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table3", "0\t0\n0\t1\n1\t0\n1\t1")
time.sleep(1) # The modification time of the 'row_policy.xml' file should be different.
copy_policy_xml('normal_filters.xml', False)
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table1", "1\t0\n1\t1")
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table2", "0\t0\t0\t0\n0\t0\t6\t0")
assert_eq_with_retry(instance, "SELECT * FROM mydb.filtered_table3", "0\t1\n1\t0")
def test_introspection():
assert instance.query("SELECT currentRowPolicies('mydb', 'filtered_table1')") == "['default']\n"
assert instance.query("SELECT currentRowPolicies('mydb', 'filtered_table2')") == "['default']\n"
assert instance.query("SELECT currentRowPolicies('mydb', 'filtered_table3')") == "['default']\n"
assert instance.query("SELECT arraySort(currentRowPolicies())") == "[('mydb','filtered_table1','default'),('mydb','filtered_table2','default'),('mydb','filtered_table3','default')]\n"
policy1 = "mydb\tfiltered_table1\tdefault\tdefault ON mydb.filtered_table1\t9e8a8f62-4965-2b5e-8599-57c7b99b3549\tusers.xml\t0\ta = 1\t\t\t\t\n"
policy2 = "mydb\tfiltered_table2\tdefault\tdefault ON mydb.filtered_table2\tcffae79d-b9bf-a2ef-b798-019c18470b25\tusers.xml\t0\ta + b < 1 or c - d > 5\t\t\t\t\n"
policy3 = "mydb\tfiltered_table3\tdefault\tdefault ON mydb.filtered_table3\t12fc5cef-e3da-3940-ec79-d8be3911f42b\tusers.xml\t0\tc = 1\t\t\t\t\n"
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs('mydb', 'filtered_table1'), id) ORDER BY table, name") == policy1
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs('mydb', 'filtered_table2'), id) ORDER BY table, name") == policy2
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs('mydb', 'filtered_table3'), id) ORDER BY table, name") == policy3
assert instance.query("SELECT * from system.row_policies ORDER BY table, name") == policy1 + policy2 + policy3
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs(), id) ORDER BY table, name") == policy1 + policy2 + policy3
def test_dcl_introspection():
assert instance.query("SHOW POLICIES ON mydb.filtered_table1") == "default\n"
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table2") == "default\n"
assert instance.query("SHOW POLICIES") == "default ON mydb.filtered_table1\ndefault ON mydb.filtered_table2\ndefault ON mydb.filtered_table3\n"
assert instance.query("SHOW POLICIES CURRENT") == "default ON mydb.filtered_table1\ndefault ON mydb.filtered_table2\ndefault ON mydb.filtered_table3\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default\n"
copy_policy_xml('all_rows.xml')
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING 1 TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING 1 TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING 1 TO default\n"
copy_policy_xml('no_rows.xml')
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING NULL TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING NULL TO default\n"
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING NULL TO default\n"
copy_policy_xml('no_filters.xml')
assert instance.query("SHOW POLICIES") == ""
def test_dcl_management():
copy_policy_xml('no_filters.xml')
assert instance.query("SHOW POLICIES") == ""
instance.query("CREATE POLICY pA ON mydb.filtered_table1 FOR SELECT USING a<b")
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == ""
assert instance.query("SHOW POLICIES ON mydb.filtered_table1") == "pA\n"
instance.query("ALTER POLICY pA ON mydb.filtered_table1 TO default")
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t1\n"
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == "pA\n"
instance.query("ALTER POLICY pA ON mydb.filtered_table1 FOR SELECT USING a>b")
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n"
instance.query("ALTER POLICY pA ON mydb.filtered_table1 RENAME TO pB")
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n"
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == "pB\n"
assert instance.query("SHOW CREATE POLICY pB ON mydb.filtered_table1") == "CREATE POLICY pB ON mydb.filtered_table1 FOR SELECT USING a > b TO default\n"
instance.query("DROP POLICY pB ON mydb.filtered_table1")
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
assert instance.query("SHOW POLICIES") == ""
def test_users_xml_is_readonly():
assert re.search("storage is readonly", instance.query_and_get_error("DROP POLICY default ON mydb.filtered_table1"))

View File

@ -50,6 +50,65 @@ def get_used_disks_for_table(node, table_name):
return node.query("select disk_name from system.parts where table == '{}' and active=1 order by modification_time".format(table_name)).strip().split('\n') return node.query("select disk_name from system.parts where table == '{}' and active=1 order by modification_time".format(table_name)).strip().split('\n')
@pytest.mark.parametrize("name,engine,alter", [
("mt_test_rule_with_invalid_destination","MergeTree()",0),
("replicated_mt_test_rule_with_invalid_destination","ReplicatedMergeTree('/clickhouse/replicated_test_rule_with_invalid_destination', '1')",0),
("mt_test_rule_with_invalid_destination","MergeTree()",1),
("replicated_mt_test_rule_with_invalid_destination","ReplicatedMergeTree('/clickhouse/replicated_test_rule_with_invalid_destination', '1')",1),
])
def test_rule_with_invalid_destination(started_cluster, name, engine, alter):
try:
def get_command(x, policy):
x = x or ""
if alter and x:
return """
ALTER TABLE {name} MODIFY TTL {expression}
""".format(expression=x, name=name)
else:
return """
CREATE TABLE {name} (
s1 String,
d1 DateTime
) ENGINE = {engine}
ORDER BY tuple()
{expression}
SETTINGS storage_policy='{policy}'
""".format(expression=x, name=name, engine=engine, policy=policy)
if alter:
node1.query(get_command(None, "small_jbod_with_external"))
with pytest.raises(QueryRuntimeException):
node1.query(get_command("TTL d1 TO DISK 'unknown'", "small_jbod_with_external"))
node1.query("DROP TABLE IF EXISTS {}".format(name))
if alter:
node1.query(get_command(None, "small_jbod_with_external"))
with pytest.raises(QueryRuntimeException):
node1.query(get_command("TTL d1 TO VOLUME 'unknown'", "small_jbod_with_external"))
node1.query("DROP TABLE IF EXISTS {}".format(name))
if alter:
node1.query(get_command(None, "only_jbod2"))
with pytest.raises(QueryRuntimeException):
node1.query(get_command("TTL d1 TO DISK 'jbod1'", "only_jbod2"))
node1.query("DROP TABLE IF EXISTS {}".format(name))
if alter:
node1.query(get_command(None, "only_jbod2"))
with pytest.raises(QueryRuntimeException):
node1.query(get_command("TTL d1 TO VOLUME 'external'", "only_jbod2"))
finally:
node1.query("DROP TABLE IF EXISTS {}".format(name))
@pytest.mark.parametrize("name,engine,positive", [ @pytest.mark.parametrize("name,engine,positive", [
("mt_test_inserts_to_disk_do_not_work","MergeTree()",0), ("mt_test_inserts_to_disk_do_not_work","MergeTree()",0),
("replicated_mt_test_inserts_to_disk_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_inserts_to_disk_do_not_work', '1')",0), ("replicated_mt_test_inserts_to_disk_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_inserts_to_disk_do_not_work', '1')",0),

View File

@ -12,7 +12,7 @@
</main_metric> </main_metric>
<preconditions> <preconditions>
<table_exists>default.hits_10m_single</table_exists> <table_exists>hits_10m_single</table_exists>
</preconditions> </preconditions>
<create_query>CREATE TABLE hits_10m_words (word String, UserID UInt64) ENGINE Memory</create_query> <create_query>CREATE TABLE hits_10m_words (word String, UserID UInt64) ENGINE Memory</create_query>

View File

@ -1,32 +0,0 @@
-- PREWHERE should fail
1 0
1 1
0 0 0 0
0 0 6 0
0 1
1 0
1
1
0
1
1
1
1
1
0
1
1
0
1
1
1
1
0
1
1
1
1
1
1 0 1 1
1 1 1 1
1 1 1 0

View File

@ -1,46 +0,0 @@
SET any_join_distinct_right_table_keys = 1;
DROP TABLE IF EXISTS test.filtered_table1;
DROP TABLE IF EXISTS test.filtered_table2;
DROP TABLE IF EXISTS test.filtered_table3;
-- Filter: a = 1, values: (1, 0), (1, 1)
CREATE TABLE test.filtered_table1 (a UInt8, b UInt8) ENGINE MergeTree ORDER BY a;
INSERT INTO test.filtered_table1 values (0, 0), (0, 1), (1, 0), (1, 1);
-- Filter: a + b < 1 or c - d > 5, values: (0, 0, 0, 0), (0, 0, 6, 0)
CREATE TABLE test.filtered_table2 (a UInt8, b UInt8, c UInt8, d UInt8) ENGINE MergeTree ORDER BY a;
INSERT INTO test.filtered_table2 values (0, 0, 0, 0), (1, 2, 3, 4), (4, 3, 2, 1), (0, 0, 6, 0);
-- Filter: c = 1, values: (0, 1), (1, 0)
CREATE TABLE test.filtered_table3 (a UInt8, b UInt8, c UInt16 ALIAS a + b) ENGINE MergeTree ORDER BY a;
INSERT INTO test.filtered_table3 values (0, 0), (0, 1), (1, 0), (1, 1);
SELECT '-- PREWHERE should fail';
SELECT * FROM test.filtered_table1 PREWHERE 1; -- { serverError 182 }
SELECT * FROM test.filtered_table2 PREWHERE 1; -- { serverError 182 }
SELECT * FROM test.filtered_table3 PREWHERE 1; -- { serverError 182 }
SELECT * FROM test.filtered_table1;
SELECT * FROM test.filtered_table2;
SELECT * FROM test.filtered_table3;
SELECT a FROM test.filtered_table1;
SELECT b FROM test.filtered_table1;
SELECT a FROM test.filtered_table1 WHERE a = 1;
SELECT a = 1 FROM test.filtered_table1;
SELECT a FROM test.filtered_table3;
SELECT b FROM test.filtered_table3;
SELECT c FROM test.filtered_table3;
SELECT a + b FROM test.filtered_table3;
SELECT a FROM test.filtered_table3 WHERE c = 1;
SELECT c = 1 FROM test.filtered_table3;
SELECT a + b = 1 FROM test.filtered_table3;
SELECT * FROM test.filtered_table1 as t1 ANY LEFT JOIN test.filtered_table1 as t2 ON t1.a = t2.b;
SELECT * FROM test.filtered_table1 as t2 ANY RIGHT JOIN test.filtered_table1 as t1 ON t2.b = t1.a;
DROP TABLE test.filtered_table1;
DROP TABLE test.filtered_table2;
DROP TABLE test.filtered_table3;

View File

@ -0,0 +1,2 @@
SHOW POLICIES;
CREATE POLICY p1 ON dummytable; -- { serverError 497 }

View File

@ -0,0 +1,32 @@
left
0 a1
1 a2
2 a3 b1
2 a3 b2
3 a4
4 a5 b3
4 a5 b4
4 a5 b5
inner
2 a3 b1
2 a3 b2
4 a5 b3
4 a5 b4
4 a5 b5
right
2 a3 b1
2 a3 b2
4 a5 b3
4 a5 b4
4 a5 b5
5 b6
full
0 a1
1 a2
2 a3 b1
2 a3 b2
3 a4
4 a5 b3
4 a5 b4
4 a5 b5
5 b6

View File

@ -0,0 +1,56 @@
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS left_join;
DROP TABLE IF EXISTS inner_join;
DROP TABLE IF EXISTS right_join;
DROP TABLE IF EXISTS full_join;
CREATE TABLE t1 (x UInt32, str String) engine = Memory;
CREATE TABLE left_join (x UInt32, s String) engine = Join(ALL, LEFT, x);
CREATE TABLE inner_join (x UInt32, s String) engine = Join(ALL, INNER, x);
CREATE TABLE right_join (x UInt32, s String) engine = Join(ALL, RIGHT, x);
CREATE TABLE full_join (x UInt32, s String) engine = Join(ALL, FULL, x);
INSERT INTO t1 (x, str) VALUES (0, 'a1'), (1, 'a2'), (2, 'a3'), (3, 'a4'), (4, 'a5');
INSERT INTO left_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
INSERT INTO inner_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
INSERT INTO right_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
INSERT INTO full_join (x, s) VALUES (2, 'b1'), (2, 'b2'), (4, 'b3'), (4, 'b4'), (4, 'b5'), (5, 'b6');
SET join_use_nulls = 0;
SELECT 'left';
SELECT * FROM t1 LEFT JOIN left_join j USING(x) ORDER BY x, str, s;
SELECT 'inner';
SELECT * FROM t1 INNER JOIN inner_join j USING(x) ORDER BY x, str, s;
SELECT 'right';
SELECT * FROM t1 RIGHT JOIN right_join j USING(x) ORDER BY x, str, s;
SELECT 'full';
SELECT * FROM t1 FULL JOIN full_join j USING(x) ORDER BY x, str, s;
-- TODO
-- SET join_use_nulls = 1;
--
-- SELECT 'left (join_use_nulls)';
-- SELECT * FROM t1 LEFT JOIN left_join j USING(x) ORDER BY x, str, s;
--
-- SELECT 'inner (join_use_nulls)';
-- SELECT * FROM t1 INNER JOIN inner_join j USING(x) ORDER BY x, str, s;
--
-- SELECT 'right (join_use_nulls)';
-- SELECT * FROM t1 RIGHT JOIN right_join j USING(x) ORDER BY x, str, s;
--
-- SELECT 'full (join_use_nulls)';
-- SELECT * FROM t1 FULL JOIN full_join j USING(x) ORDER BY x, str, s;
DROP TABLE t1;
DROP TABLE left_join;
DROP TABLE inner_join;
DROP TABLE right_join;
DROP TABLE full_join;

View File

@ -16,7 +16,6 @@ any right
5 b6 5 b6
semi left semi left
2 a3 b1 2 a3 b1
2 a6 b1
4 a5 b3 4 a5 b3
semi right semi right
2 a3 b1 2 a3 b1

View File

@ -45,9 +45,6 @@ SELECT * FROM t1 ANY INNER JOIN any_inner_join j USING(x) ORDER BY x, str, s;
SELECT 'any right'; SELECT 'any right';
SELECT * FROM t1 ANY RIGHT JOIN any_right_join j USING(x) ORDER BY x, str, s; SELECT * FROM t1 ANY RIGHT JOIN any_right_join j USING(x) ORDER BY x, str, s;
INSERT INTO t1 (x, str) VALUES (2, 'a6');
SELECT 'semi left'; SELECT 'semi left';
SELECT * FROM t1 SEMI LEFT JOIN semi_left_join j USING(x) ORDER BY x, str, s; SELECT * FROM t1 SEMI LEFT JOIN semi_left_join j USING(x) ORDER BY x, str, s;

View File

@ -5,6 +5,7 @@
"docker/test/compatibility/ubuntu": "yandex/clickhouse-test-old-ubuntu", "docker/test/compatibility/ubuntu": "yandex/clickhouse-test-old-ubuntu",
"docker/test/integration": "yandex/clickhouse-integration-test", "docker/test/integration": "yandex/clickhouse-integration-test",
"docker/test/performance": "yandex/clickhouse-performance-test", "docker/test/performance": "yandex/clickhouse-performance-test",
"docker/test/performance-comparison": "yandex/clickhouse-performance-comparison",
"docker/test/pvs": "yandex/clickhouse-pvs-test", "docker/test/pvs": "yandex/clickhouse-pvs-test",
"docker/test/stateful": "yandex/clickhouse-stateful-test", "docker/test/stateful": "yandex/clickhouse-stateful-test",
"docker/test/stateful_with_coverage": "yandex/clickhouse-stateful-test-with-coverage", "docker/test/stateful_with_coverage": "yandex/clickhouse-stateful-test-with-coverage",

View File

@ -0,0 +1,17 @@
# docker build -t yandex/clickhouse-performance-comparison .
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
p7zip-full bash ncdu wget python3 python3-pip python3-dev g++ \
&& pip3 --no-cache-dir install clickhouse_driver \
&& apt-get purge --yes python3-dev g++ \
&& apt-get autoremove --yes \
&& apt-get clean
COPY * /
CMD /entrypoint.sh
# docker run --network=host --volume <workspace>:/workspace --volume=<output>:/output -e LEFT_PR=<> -e LEFT_SHA=<> -e RIGHT_PR=<> -e RIGHT_SHA=<> yandex/clickhouse-performance-comparison

View File

@ -4,8 +4,8 @@ set -o pipefail
trap "exit" INT TERM trap "exit" INT TERM
trap "kill 0" EXIT trap "kill 0" EXIT
mkdir left ||: script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
mkdir right ||:
mkdir db0 ||: mkdir db0 ||:
left_pr=$1 left_pr=$1
@ -16,43 +16,53 @@ right_sha=$4
function download function download
{ {
rm -r left ||:
mkdir left ||:
rm -r right ||:
mkdir right ||:
la="$left_pr-$left_sha.tgz" la="$left_pr-$left_sha.tgz"
ra="$right_pr-$right_sha.tgz" ra="$right_pr-$right_sha.tgz"
wget -nd -c "https://clickhouse-builds.s3.yandex.net/$left_pr/$left_sha/performance/performance.tgz" -O "$la" && tar -C left --strip-components=1 -zxvf "$la" & wget -q -nd -c "https://clickhouse-builds.s3.yandex.net/$left_pr/$left_sha/performance/performance.tgz" -O "$la" && tar -C left --strip-components=1 -zxvf "$la" &
wget -nd -c "https://clickhouse-builds.s3.yandex.net/$right_pr/$right_sha/performance/performance.tgz" -O "$ra" && tar -C right --strip-components=1 -zxvf "$ra" & wget -q -nd -c "https://clickhouse-builds.s3.yandex.net/$right_pr/$right_sha/performance/performance.tgz" -O "$ra" && tar -C right --strip-components=1 -zxvf "$ra" &
cd db0 && wget -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_10m_single/partitions/hits_10m_single.tar" && tar -xvf hits_10m_single.tar & cd db0 && wget -q -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_10m_single/partitions/hits_10m_single.tar" && tar -xvf hits_10m_single.tar &
cd db0 && wget -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_100m_single/partitions/hits_100m_single.tar" && tar -xvf hits_100m_single.tar & cd db0 && wget -q -nd -c "https://s3.mds.yandex.net/clickhouse-private-datasets/hits_100m_single/partitions/hits_100m_single.tar" && tar -xvf hits_100m_single.tar &
#cd db0 && wget -nd -c "https://clickhouse-datasets.s3.yandex.net/hits/partitions/hits_v1.tar" && tar -xvf hits_v1.tar & cd db0 && wget -q -nd -c "https://clickhouse-datasets.s3.yandex.net/hits/partitions/hits_v1.tar" && tar -xvf hits_v1.tar &
wait wait
# Use hardlinks instead of copying # Use hardlinks instead of copying
rm -r left/db ||:
rm -r right/db ||:
cp -al db0/ left/db/ cp -al db0/ left/db/
cp -al db0/ right/db/ cp -al db0/ right/db/
} }
download
#download
function configure function configure
{ {
sed -i 's/<tcp_port>9000/<tcp_port>9001/g' right/config/config.xml sed -i 's/<tcp_port>9000/<tcp_port>9001/g' right/config/config.xml
cat > right/config/config.d/perf-test-tweaks.xml <<EOF cat > right/config/config.d/zz-perf-test-tweaks.xml <<EOF
<yandex> <yandex>
<logger> <logger>
<console>true</console> <console>true</console>
</logger> </logger>
<text_log remove="remove"/> <text_log remove="remove">
<table remove="remove"/>
</text_log>
<metric_log remove="remove">
<table remove="remove"/>
</metric_log>
</yandex> </yandex>
EOF EOF
cp right/config/config.d/perf-test-tweaks.xml left/config/config.d/perf-test-tweaks.xml cp right/config/config.d/zz-perf-test-tweaks.xml left/config/config.d/zz-perf-test-tweaks.xml
rm left/config/config.d/metric_log.xml ||:
rm left/config/config.d/text_log.xml ||:
rm right/config/config.d/metric_log.xml ||:
rm right/config/config.d/text_log.xml ||:
} }
configure configure
function restart function restart
{ {
while killall clickhouse ; do echo . ; sleep 1 ; done while killall clickhouse ; do echo . ; sleep 1 ; done
@ -78,20 +88,31 @@ function restart
while ! right/clickhouse client --port 9001 --query "select 1" ; do kill -0 $right_pid ; echo . ; sleep 1 ; done while ! right/clickhouse client --port 9001 --query "select 1" ; do kill -0 $right_pid ; echo . ; sleep 1 ; done
echo right ok echo right ok
}
right/clickhouse client --port 9001 --query "create database test" ||:
right/clickhouse client --port 9001 --query "rename table datasets.hits_v1 to test.hits" ||:
left/clickhouse client --port 9000 --query "create database test" ||:
left/clickhouse client --port 9000 --query "rename table datasets.hits_v1 to test.hits" ||:
}
restart restart
for test in ch/dbms/tests/performance/*.xml function run_tests
do {
test_name=$(basename $test ".xml") # Just check that the script runs at all
./perf.py "$test" > "$test_name-raw.tsv" || continue "$script_dir/perf.py" --help > /dev/null
right/clickhouse local --file "$test_name-raw.tsv" --structure 'query text, run int, version UInt32, time float' --query "$(cat eqmed.sql)" > "$test_name-report.tsv"
done
#while killall clickhouse ; do echo . ; sleep 1 ; done
#echo ok
# Run the tests
for test in left/performance/*.xml
do
test_name=$(basename $test ".xml")
"$script_dir/perf.py" "$test" > "$test_name-raw.tsv" 2> "$test_name-err.log" || continue
right/clickhouse local --file "$test_name-raw.tsv" --structure 'query text, run int, version UInt32, time float' --query "$(cat $script_dir/eqmed.sql)" > "$test_name-report.tsv"
done
}
run_tests
# Analyze results
result_structure="left float, right float, diff float, rd Array(float), query text"
right/clickhouse local --file '*-report.tsv' -S "$result_structure" --query "select * from table where rd[3] > 0.05 order by rd[3] desc" > flap-prone.tsv
right/clickhouse local --file '*-report.tsv' -S "$result_structure" --query "select * from table where diff > 0.05 and diff > rd[3] order by diff desc" > bad-perf.tsv
grep Exception:[^:] *-err.log > run-errors.log

View File

@ -0,0 +1,8 @@
#!/bin/bash
cd /workspace
../compare.sh $LEFT_PR $LEFT_SHA $RIGHT_PR $RIGHT_SHA > compare.log 2>&1
7z a /output/output.7z *.log *.tsv
cp compare.log /output

View File

@ -1,6 +1,6 @@
-- input is table(query text, run UInt32, version int, time float) -- input is table(query text, run UInt32, version int, time float)
select select
abs(diff_percent) > rd_quantiles_percent[3] fail, -- abs(diff_percent) > rd_quantiles_percent[3] fail,
floor(original_medians_array.time_by_version[1], 4) m1, floor(original_medians_array.time_by_version[1], 4) m1,
floor(original_medians_array.time_by_version[2], 4) m2, floor(original_medians_array.time_by_version[2], 4) m2,
floor((m1 - m2) / m1, 3) diff_percent, floor((m1 - m2) / m1, 3) diff_percent,
@ -38,4 +38,4 @@ from
group by query group by query
) original_medians_array ) original_medians_array
where rd.query = original_medians_array.query where rd.query = original_medians_array.query
order by fail desc, rd_quantiles_percent[3] asc; order by rd_quantiles_percent[3] desc;

View File

@ -14,9 +14,14 @@ tree = et.parse(args.file[0])
root = tree.getroot() root = tree.getroot()
# Check main metric # Check main metric
main_metric = root.find('main_metric/*').tag main_metric_element = root.find('main_metric/*')
if main_metric != 'min_time': if main_metric_element is not None and main_metric_element.tag != 'min_time':
raise Exception('Only the min_time main metric is supported. This test uses \'{}\''.format(main_metric)) raise Exception('Only the min_time main metric is supported. This test uses \'{}\''.format(main_metric_element.tag))
# FIXME another way to detect infinite tests. They should have an appropriate main_metric but sometimes they don't.
infinite_sign = root.find('.//average_speed_not_changing_for_ms')
if infinite_sign is not None:
raise Exception('Looks like the test is infinite (sign 1)')
# Open connections # Open connections
servers = [{'host': 'localhost', 'port': 9000, 'client_name': 'left'}, {'host': 'localhost', 'port': 9001, 'client_name': 'right'}] servers = [{'host': 'localhost', 'port': 9000, 'client_name': 'left'}, {'host': 'localhost', 'port': 9001, 'client_name': 'right'}]
@ -24,12 +29,15 @@ connections = [clickhouse_driver.Client(**server) for server in servers]
# Check tables that should exist # Check tables that should exist
tables = [e.text for e in root.findall('preconditions/table_exists')] tables = [e.text for e in root.findall('preconditions/table_exists')]
if tables: for t in tables:
for c in connections: for c in connections:
tables_list = ", ".join("'{}'".format(t) for t in tables) res = c.execute("select 1 from {}".format(t))
res = c.execute("select t from values('t text', {}) anti join system.tables on database = currentDatabase() and name = t".format(tables_list))
if res: # Apply settings
raise Exception('Some tables are not found: {}'.format(res)) settings = root.findall('settings/*')
for c in connections:
for s in settings:
c.execute("set {} = '{}'".format(s.tag, s.text))
# Process substitutions # Process substitutions
subst_elems = root.findall('substitutions/substitution') subst_elems = root.findall('substitutions/substitution')
@ -70,6 +78,9 @@ for c in connections:
c.execute(q) c.execute(q)
# Run test queries # Run test queries
def tsv_escape(s):
return s.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n').replace('\r','')
test_query_templates = [q.text for q in root.findall('query')] test_query_templates = [q.text for q in root.findall('query')]
test_queries = substitute_parameters(test_query_templates, parameter_combinations) test_queries = substitute_parameters(test_query_templates, parameter_combinations)
@ -77,7 +88,7 @@ for q in test_queries:
for run in range(0, 7): for run in range(0, 7):
for conn_index, c in enumerate(connections): for conn_index, c in enumerate(connections):
res = c.execute(q) res = c.execute(q)
print(q + '\t' + str(run) + '\t' + str(conn_index) + '\t' + str(c.last_query.elapsed)) print(tsv_escape(q) + '\t' + str(run) + '\t' + str(conn_index) + '\t' + str(c.last_query.elapsed))
# Run drop queries # Run drop queries
drop_query_templates = [q.text for q in root.findall('drop_query')] drop_query_templates = [q.text for q in root.findall('drop_query')]

View File

@ -21,4 +21,36 @@ If you use Oracle through the ODBC driver as a source of external dictionaries,
NLS_LANG=RUSSIAN_RUSSIA.UTF8 NLS_LANG=RUSSIAN_RUSSIA.UTF8
``` ```
## How to export data from ClickHouse to the file?
### Using INTO OUTFILE Clause
Add [INTO OUTFILE](../query_language/select/#into-outfile-clause) clause to your query.
For example:
```sql
SELECT * FROM table INTO OUTFILE 'file'
```
By default, ClickHouse uses the [TabSeparated](../interfaces/formats.md#tabseparated) format for output data. To select the [data format](../interfaces/formats.md), use the [FORMAT clause](../query_language/select/#format-clause).
For example:
```sql
SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV
```
### Using File-engine Table
See [File](../operations/table_engines/file.md).
### Using Command-line Redirection
```sql
$ clickhouse-client --query "SELECT * from table" > result.txt
```
See [clickhouse-client](../interfaces/cli.md).
[Original article](https://clickhouse.yandex/docs/en/faq/general/) <!--hide--> [Original article](https://clickhouse.yandex/docs/en/faq/general/) <!--hide-->

View File

@ -29,6 +29,7 @@ The supported formats are:
| [PrettySpace](#prettyspace) | ✗ | ✔ | | [PrettySpace](#prettyspace) | ✗ | ✔ |
| [Protobuf](#protobuf) | ✔ | ✔ | | [Protobuf](#protobuf) | ✔ | ✔ |
| [Parquet](#data-format-parquet) | ✔ | ✔ | | [Parquet](#data-format-parquet) | ✔ | ✔ |
| [ORC](#data-format-orc) | ✔ | ✗ |
| [RowBinary](#rowbinary) | ✔ | ✔ | | [RowBinary](#rowbinary) | ✔ | ✔ |
| [RowBinaryWithNamesAndTypes](#rowbinarywithnamesandtypes) | ✔ | ✔ | | [RowBinaryWithNamesAndTypes](#rowbinarywithnamesandtypes) | ✔ | ✔ |
| [Native](#native) | ✔ | ✔ | | [Native](#native) | ✔ | ✔ |
@ -954,16 +955,57 @@ Data types of a ClickHouse table columns can differ from the corresponding field
You can insert Parquet data from a file into ClickHouse table by the following command: You can insert Parquet data from a file into ClickHouse table by the following command:
```bash ```bash
cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT Parquet" $ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT Parquet"
``` ```
You can select data from a ClickHouse table and save them into some file in the Parquet format by the following command: You can select data from a ClickHouse table and save them into some file in the Parquet format by the following command:
```sql ```bash
clickhouse-client --query="SELECT * FROM {some_table} FORMAT Parquet" > {some_file.pq} $ clickhouse-client --query="SELECT * FROM {some_table} FORMAT Parquet" > {some_file.pq}
``` ```
To exchange data with the Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md). To exchange data with Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md).
## ORC {#data-format-orc}
[Apache ORC](https://orc.apache.org/) is a columnar storage format widespread in the Hadoop ecosystem. ClickHouse supports only read operations for this format.
### Data Types Matching
The table below shows supported data types and how they match ClickHouse [data types](../data_types/index.md) in `INSERT` queries.
| ORC data type (`INSERT`) | ClickHouse data type |
| -------------------- | ------------------ |
| `UINT8`, `BOOL` | [UInt8](../data_types/int_uint.md) |
| `INT8` | [Int8](../data_types/int_uint.md) |
| `UINT16` | [UInt16](../data_types/int_uint.md) |
| `INT16` | [Int16](../data_types/int_uint.md) |
| `UINT32` | [UInt32](../data_types/int_uint.md) |
| `INT32` | [Int32](../data_types/int_uint.md) |
| `UINT64` | [UInt64](../data_types/int_uint.md) |
| `INT64` | [Int64](../data_types/int_uint.md) |
| `FLOAT`, `HALF_FLOAT` | [Float32](../data_types/float.md) |
| `DOUBLE` | [Float64](../data_types/float.md) |
| `DATE32` | [Date](../data_types/date.md) |
| `DATE64`, `TIMESTAMP` | [DateTime](../data_types/datetime.md) |
| `STRING`, `BINARY` | [String](../data_types/string.md) |
| `DECIMAL` | [Decimal](../data_types/decimal.md) |
ClickHouse supports configurable precision of `Decimal` type. The `INSERT` query treats the ORC `DECIMAL` type as the ClickHouse `Decimal128` type.
Unsupported ORC data types: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`.
Data types of a ClickHouse table columns can differ from the corresponding fields of the ORC data inserted. When inserting data, ClickHouse interprets data types according to the table above and then [cast](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) the data to that data type which is set for the ClickHouse table column.
### Inserting Data
You can insert Parquet data from a file into ClickHouse table by the following command:
```bash
$ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT ORC"
```
To exchange data with Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md).
## Format Schema {#formatschema} ## Format Schema {#formatschema}

View File

@ -1120,7 +1120,7 @@ The structure of results (the number and type of columns) must match for the que
Queries that are parts of UNION ALL can't be enclosed in brackets. ORDER BY and LIMIT are applied to separate queries, not to the final result. If you need to apply a conversion to the final result, you can put all the queries with UNION ALL in a subquery in the FROM clause. Queries that are parts of UNION ALL can't be enclosed in brackets. ORDER BY and LIMIT are applied to separate queries, not to the final result. If you need to apply a conversion to the final result, you can put all the queries with UNION ALL in a subquery in the FROM clause.
### INTO OUTFILE Clause ### INTO OUTFILE Clause {#into-outfile-clause}
Add the `INTO OUTFILE filename` clause (where filename is a string literal) to redirect query output to the specified file. Add the `INTO OUTFILE filename` clause (where filename is a string literal) to redirect query output to the specified file.
In contrast to MySQL, the file is created on the client side. The query will fail if a file with the same filename already exists. In contrast to MySQL, the file is created on the client side. The query will fail if a file with the same filename already exists.
@ -1128,7 +1128,7 @@ This functionality is available in the command-line client and clickhouse-local
The default output format is TabSeparated (the same as in the command-line client batch mode). The default output format is TabSeparated (the same as in the command-line client batch mode).
### FORMAT Clause ### FORMAT Clause {#format-clause}
Specify 'FORMAT format' to get data in any specified format. Specify 'FORMAT format' to get data in any specified format.
You can use this for convenience, or for creating dumps. You can use this for convenience, or for creating dumps.

View File

@ -107,7 +107,7 @@ Using libraries from OS packages is discouraged, but we also support this option
- shared; - shared;
Static linking is the only option for production usage. Static linking is the only option for production usage.
We also have support for shared linking, but it is indended only for developers. We also have support for shared linking, but it is intended only for developers.
#### Make tools: #### Make tools:
- make; - make;