/* Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NDB_PACK_HPP #define NDB_PACK_HPP #include #include #include #include #include class NdbOut; /* * Pack an array of NDB data values. The types are specified by an * array of data types. There is no associated table or attribute ids. * All or an initial sequence of the specified values are present. * * Currently used for ordered index keys and bounds in kernel (DBTUX) * and in index statistics (mysqld). The comparison methods use the * primitive type comparisons from NdbSqlUtil. * * Keys and bounds use same spec. However a value in an index bound can * be NULL even if the key attribute is not nullable. Therefore bounds * set the "allNullable" property and have a longer null mask. * * There are two distinct use occasions: 1) construction of data or * bound 2) operating on previously constructed data or bound. There * are classes Data/DataC and Bound/BoundC for these uses. The latter * often can return a result without interpreting the full value. * * Methods return -1 on error and 0 on success. Comparison methods * assume well-formed data and return negative, zero, positive for less, * equal, greater. */ class NdbPack { public: class Endian; class Type; class Spec; class Iter; class DataC; class Data; class BoundC; class Bound; /* * Get SQL type. */ static const NdbSqlUtil::Type& getSqlType(Uint32 typeId); /* * Error codes for core dumps. */ class Error { public: enum { TypeNotSet = -101, // type id was not set TypeOutOfRange = -102, // type id is out of range TypeNotSupported = -103, // blob (and for now bit) types TypeSizeZero = -104, // max size was set to zero TypeFixSizeInvalid = -105, // fixed size specified wrong TypeNullableNotBool = -106, // nullable must be 0 or 1 CharsetNotSpecified = -107, // char type with no charset number CharsetNotFound = -108, // cannot install in all_charsets[] CharsetNotAllowed = -109, // non-char type with charset SpecBufOverflow = -201, // more spec items than allocated DataCntOverflow = -301, // more data items than in spec DataBufOverflow = -302, // more data bytes than allocated DataValueOverflow = -303, // var length exceeds max size DataNotNullable = -304, // NULL value to not-nullable type InvalidAttrInfo = -305, // invalid plain old attr info BoundEmptySide = -401, // side not 0 for empty bound BoundNonemptySide = -402, // side not -1,+1 for non-empty bound InternalError = -901, ValidationError = -902, NoError = 0 }; Error(); ~Error() {} int get_error_code() const; int get_error_line() const; private: friend class Endian; friend class Type; friend class Spec; friend class Iter; friend class DataC; friend class Data; friend class BoundC; friend class Bound; void set_error(int code, int line) const; void set_error(const Error& e2) const; mutable int m_error_code; mutable int m_error_line; }; /* * Endian definitions. */ class Endian { public: enum Value { Native = 0, // replaced by actual value Little = 1, Big = 2 }; static Value get_endian(); static void convert(void* ptr, Uint32 len); }; /* * Data type. */ class Type : public Error { public: Type(); Type(int typeId, Uint32 byteSize, bool nullable, Uint32 csNumber); ~Type() {} /* * Define the type. Size is fixed or max size. Values of variable * length have length bytes. The definition is verified when the * type is added to the specification. This also installs missing * CHARSET_INFO* into all_charsets[]. */ void set(Uint32 typeId, Uint32 byteSize, bool nullable, Uint32 csNumber); // getters Uint32 get_type_id() const; Uint32 get_byte_size() const; bool get_nullable() const; Uint32 get_cs_number() const; Uint32 get_array_type() const; // print friend NdbOut& operator<<(NdbOut&, const Type&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz) const; int validate() const; private: friend class Spec; friend class Iter; friend class DataC; friend class Data; // verify and complete when added to specification int complete(); Uint16 m_typeId; Uint16 m_byteSize; // fixed or max size in bytes Uint16 m_nullable; Uint16 m_csNumber; Uint16 m_arrayType; // 0,1,2 length bytes Uint16 m_nullbitPos; // computed as part of Spec }; /* * Data specification i.e. array of types. Usually constructed on the * heap, so keep fairly small. Used for boths keys and bounds. */ class Spec : public Error { public: Spec(); ~Spec() {} // set initial buffer (calls reset) void set_buf(Type* buf, Uint32 bufMaxCnt); // use if buffer is relocated void set_buf(Type* buf); // reset but keep buffer void reset(); // add type to specification once or number of times int add(Type type); int add(Type type, Uint32 cnt); // copy from void copy(const Spec& s2); // getters (bounds set allNullable) const Type& get_type(Uint32 i) const; Uint32 get_cnt() const; Uint32 get_nullable_cnt(bool allNullable) const; Uint32 get_nullmask_len(bool allNullable) const; // max data length including null mask Uint32 get_max_data_len(bool allNullable) const; // minimum var bytes (if used by Data instance) Uint32 get_min_var_bytes(bool allNullable) const; // print friend NdbOut& operator<<(NdbOut&, const Spec&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz) const; int validate() const; private: friend class Iter; friend class DataC; friend class Data; friend class BoundC; // undefined Spec(const Spec&); Spec& operator=(const Spec&); Type* m_buf; Uint16 m_bufMaxCnt; Uint16 m_cnt; Uint16 m_nullableCnt; Uint16 m_varsizeCnt; Uint32 m_maxByteSize; // excludes null mask }; /* * Iterator over data items. DataC uses external Iter instances in * comparison methods etc. Data contains an Iter instance which * iterates on items added. */ class Iter : public Error { public: // the data instance is only used to set metadata Iter(const DataC& data); ~Iter() {} void reset(); private: friend class DataC; friend class Data; friend class BoundC; // undefined Iter(const Iter&); Iter& operator=(const Iter&); // describe next non-null or null item and advance iterator int desc(const Uint8* item); int desc_null(); // compare current items (DataC buffers are passed) int cmp(const Iter& r2, const Uint8* buf1, const Uint8* buf2) const; const Spec& m_spec; const bool m_allNullable; // iterator Uint32 m_itemPos; // position of current item in DataC buffer Uint32 m_cnt; // number of items described so far Uint32 m_nullCnt; // current item Uint32 m_lenBytes; // 0-2 Uint32 m_bareLen; // excludes length bytes Uint32 m_itemLen; // full length, value zero means null }; /* * Read-only superclass of Data. Initialized from a previously * constructed Data buffer (any var bytes skipped). Methods interpret * one data item at a time. Values are native endian. */ class DataC : public Error { public: DataC(const Spec& spec, bool allNullable); // set buffer to previously constructed one with given item count void set_buf(const void* buf, Uint32 bufMaxLen, Uint32 cnt); // interpret next data item int desc(Iter& r) const; // compare cnt attrs and also return number of initial equal attrs int cmp(const DataC& d2, Uint32 cnt, Uint32& num_eq) const; // getters const Spec& get_spec() const; const void* get_data_buf() const; Uint32 get_cnt() const; bool is_empty() const; bool is_full() const; // print friend NdbOut& operator<<(NdbOut&, const DataC&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz, bool convert_flag = false) const; int validate() const { return 0; } private: friend class Iter; friend class Data; friend class BoundC; // undefined DataC(const Data&); DataC& operator=(const DataC&); const Spec& m_spec; const bool m_allNullable; const Uint8* m_buf; Uint32 m_bufMaxLen; // can be updated as part of Data instance Uint32 m_cnt; }; /* * Instance of an array of data values. The values are packed into * a byte buffer. The buffer is also maintained as a single varbinary * value if non-zero var bytes (length bytes) is specified. * * Data instances can be received from another source (such as table * in database) and may not be native-endian. Such instances must * first be completed with desc_all() and convert(). */ class Data : public DataC { public: Data(const Spec& spec, bool allNullable, Uint32 varBytes); // set buffer (calls reset) void set_buf(void* buf, Uint32 bufMaxLen); // reset but keep buffer (header is zeroed) void reset(); // add non-null data items and return length in bytes int add(const void* data, Uint32* len_out); int add(const void* data, Uint32 cnt, Uint32* len_out); // add null data items and return length 0 bytes int add_null(Uint32* len_out); int add_null(Uint32 cnt, Uint32* len_out); // add from "plain old attr info" int add_poai(const Uint32* poai, Uint32* len_out); int add_poai(const Uint32* poai, Uint32 cnt, Uint32* len_out); // call this before first use int finalize(); // copy from int copy(const DataC& d2); // convert endian int convert(Endian::Value to_endian); // create complete instance from buffer contents int desc_all(Uint32 cnt, Endian::Value from_endian); // getters Uint32 get_max_len() const; Uint32 get_max_len4() const; Uint32 get_var_bytes() const; const void* get_full_buf() const; Uint32 get_full_len() const; Uint32 get_data_len() const; Uint32 get_null_cnt() const; Endian::Value get_endian() const; // print friend NdbOut& operator<<(NdbOut&, const Data&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz) const; int validate() const; private: friend class Iter; friend class Bound; // undefined Data(const Data&); Data& operator=(const Data&); int finalize_impl(); int convert_impl(Endian::Value to_endian); const Uint32 m_varBytes; Uint8* m_buf; Uint32 m_bufMaxLen; Endian::Value m_endian; // iterator on items added Iter m_iter; }; /* * Read-only superclass of BoundC, analogous to DataC. Initialized * from a previously constructed Bound or DataC buffer. */ class BoundC : public Error { public: BoundC(DataC& data); ~BoundC() {} // call this before first use int finalize(int side); // compare bound to key (may return 0 if bound is longer) int cmp(const DataC& d2, Uint32 cnt, Uint32& num_eq) const; // compare bounds (may return 0 if cnt is less than min length) int cmp(const BoundC& b2, Uint32 cnt, Uint32& num_eq) const; // getters DataC& get_data() const; int get_side() const; // print friend NdbOut& operator<<(NdbOut&, const BoundC&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz) const; int validate() const; private: friend class Bound; // undefined BoundC(const BoundC&); BoundC& operator=(const BoundC&); DataC& m_data; int m_side; }; /* * Ordered index range bound consists of a partial key and a "side". * The partial key is a Data instance where some initial number of * values are present. It is defined separately by the caller and * passed to Bound ctor by reference. */ class Bound : public BoundC { public: Bound(Data& data); ~Bound() {} void reset(); // call this before first use int finalize(int side); // getters Data& get_data() const; // print friend NdbOut& operator<<(NdbOut&, const Bound&); void print(NdbOut& out) const; const char* print(char* buf, Uint32 bufsz) const; int validate() const; private: // undefined Bound(const Bound&); Bound& operator=(const Bound&); Data& m_data; }; /* * Helper for print() methods. */ struct Print { private: friend class Endian; friend class Type; friend class Spec; friend class Iter; friend class DataC; friend class Data; friend class BoundC; friend class Bound; Print(char* buf, Uint32 bufsz); void print(const char* frm, ...); char* m_buf; Uint32 m_bufsz; Uint32 m_sz; }; }; // NdbPack inline const NdbSqlUtil::Type& NdbPack::getSqlType(Uint32 typeId) { return NdbSqlUtil::m_typeList[typeId]; } // NdbPack::Error inline NdbPack::Error::Error() { m_error_code = 0; m_error_line = 0; } // NdbPack::Endian inline NdbPack::Endian::Value NdbPack::Endian::get_endian() { #ifndef WORDS_BIGENDIAN return Little; #else return Big; #endif } // NdbPack::Type inline NdbPack::Type::Type() { m_typeId = NDB_TYPE_UNDEFINED; m_byteSize = 0; m_nullable = true; m_csNumber = 0; m_arrayType = 0; m_nullbitPos = 0; } inline NdbPack::Type::Type(int typeId, Uint32 byteSize, bool nullable, Uint32 csNumber) { set(typeId, byteSize, nullable, csNumber); } inline void NdbPack::Type::set(Uint32 typeId, Uint32 byteSize, bool nullable, Uint32 csNumber) { m_typeId = typeId; m_byteSize = byteSize; m_nullable = nullable; m_csNumber = csNumber; } inline Uint32 NdbPack::Type::get_type_id() const { return m_typeId; } inline Uint32 NdbPack::Type::get_byte_size() const { return m_byteSize; } inline bool NdbPack::Type::get_nullable() const { return (bool)m_nullable; } inline Uint32 NdbPack::Type::get_cs_number() const { return m_csNumber; } inline Uint32 NdbPack::Type::get_array_type() const { return m_arrayType; } // NdbPack::Spec inline NdbPack::Spec::Spec() { reset(); m_buf = 0; m_bufMaxCnt = 0; } inline void NdbPack::Spec::set_buf(Type* buf, Uint32 bufMaxCnt) { reset(); m_buf = buf; m_bufMaxCnt = bufMaxCnt; } inline void NdbPack::Spec::set_buf(Type* buf) { m_buf = buf; } inline void NdbPack::Spec::reset() { m_cnt = 0; m_nullableCnt = 0; m_varsizeCnt = 0; m_maxByteSize = 0; } inline const NdbPack::Type& NdbPack::Spec::get_type(Uint32 i) const { assert(i < m_cnt); return m_buf[i]; } inline Uint32 NdbPack::Spec::get_cnt() const { return m_cnt; } inline Uint32 NdbPack::Spec::get_nullable_cnt(bool allNullable) const { if (!allNullable) return m_nullableCnt; else return m_cnt; } inline Uint32 NdbPack::Spec::get_nullmask_len(bool allNullable) const { return (get_nullable_cnt(allNullable) + 7) / 8; } inline Uint32 NdbPack::Spec::get_max_data_len(bool allNullable) const { return get_nullmask_len(allNullable) + m_maxByteSize; } inline Uint32 NdbPack::Spec::get_min_var_bytes(bool allNullable) const { const Uint32 len = get_max_data_len(allNullable); return (len < 256 ? 1 : 2); } // NdbPack::Iter inline NdbPack::Iter::Iter(const DataC& data) : m_spec(data.m_spec), m_allNullable(data.m_allNullable) { reset(); } inline void NdbPack::Iter::reset() { m_itemPos = m_spec.get_nullmask_len(m_allNullable); m_cnt = 0; m_nullCnt = 0; m_lenBytes = 0; m_bareLen = 0; m_itemLen = 0; } // NdbPack::DataC inline NdbPack::DataC::DataC(const Spec& spec, bool allNullable) : m_spec(spec), m_allNullable(allNullable) { m_buf = 0; m_bufMaxLen = 0; m_cnt = 0; } inline void NdbPack::DataC::set_buf(const void* buf, Uint32 bufMaxLen, Uint32 cnt) { m_buf = static_cast(buf); m_bufMaxLen = bufMaxLen; m_cnt = cnt; } inline const NdbPack::Spec& NdbPack::DataC::get_spec() const { return m_spec; } inline const void* NdbPack::DataC::get_data_buf() const { return &m_buf[0]; } inline Uint32 NdbPack::DataC::get_cnt() const { return m_cnt; } inline bool NdbPack::DataC::is_empty() const { return m_cnt == 0; } inline bool NdbPack::DataC::is_full() const { return m_cnt == m_spec.m_cnt; } // NdbPack::Data inline NdbPack::Data::Data(const Spec& spec, bool allNullable, Uint32 varBytes) : DataC(spec, allNullable), m_varBytes(varBytes), m_iter(*this) { m_buf = 0; m_bufMaxLen = 0; m_endian = Endian::get_endian(); } inline void NdbPack::Data::set_buf(void* buf, Uint32 bufMaxLen) { m_buf = static_cast(buf); m_bufMaxLen = bufMaxLen; reset(); assert(bufMaxLen >= m_varBytes); DataC::set_buf(&m_buf[m_varBytes], m_bufMaxLen - m_varBytes, 0); } inline void NdbPack::Data::reset() { m_cnt = 0; // in DataC const Uint32 bytes = m_varBytes + m_spec.get_nullmask_len(m_allNullable); memset(m_buf, 0, bytes); m_endian = Endian::get_endian(); m_iter.reset(); } inline int NdbPack::Data::finalize() { if (m_varBytes == 0 || finalize_impl() == 0) return 0; return -1; } inline int NdbPack::Data::convert(Endian::Value to_endian) { if (to_endian == Endian::Native) to_endian = Endian::get_endian(); if (m_endian == to_endian) return 0; if (convert_impl(to_endian) == 0) { m_endian = to_endian; return 0; } return -1; } inline Uint32 NdbPack::Data::get_max_len() const { return m_varBytes + m_spec.get_max_data_len(m_allNullable); } inline Uint32 NdbPack::Data::get_max_len4() const { Uint32 len4 = get_max_len(); len4 += 3; len4 /= 4; len4 *= 4; return len4; } inline Uint32 NdbPack::Data::get_var_bytes() const { return m_varBytes; } inline const void* NdbPack::Data::get_full_buf() const { return &m_buf[0]; } inline Uint32 NdbPack::Data::get_full_len() const { return m_varBytes + m_iter.m_itemPos + m_iter.m_itemLen; } inline Uint32 NdbPack::Data::get_data_len() const { return m_iter.m_itemPos + m_iter.m_itemLen; } inline Uint32 NdbPack::Data::get_null_cnt() const { return m_iter.m_nullCnt; } inline NdbPack::Endian::Value NdbPack::Data::get_endian() const { return m_endian; } // NdbPack::BoundC inline NdbPack::BoundC::BoundC(DataC& data) : m_data(data) { m_side = 0; } inline int NdbPack::BoundC::cmp(const DataC& d2, Uint32 cnt, Uint32& num_eq) const { const BoundC& b1 = *this; const DataC& d1 = b1.m_data; int res = d1.cmp(d2, cnt, num_eq); if (res == 0 && d1.m_cnt <= d2.m_cnt) res = b1.m_side; return res; } inline NdbPack::DataC& NdbPack::BoundC::get_data() const { return m_data; } inline int NdbPack::BoundC::get_side() const { return m_side; } // NdbPack::Bound inline NdbPack::Bound::Bound(Data& data) : BoundC(data), m_data(data) { } inline void NdbPack::Bound::reset() { m_data.reset(); m_side = 0; } inline int NdbPack::Bound::finalize(int side) { if (m_data.finalize() == -1) { set_error(m_data); return -1; } if (BoundC::finalize(side) == -1) return -1; return 0; } inline NdbPack::Data& NdbPack::Bound::get_data() const { return m_data; } #endif // NDB_PACK_HPP