blob: 235867221dcf2fdb1fd7982ec8d320050f7c1a1d [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef IMPALA_UDF_UDF_H
#define IMPALA_UDF_UDF_H
// THIS FILE IS USED BY THE STANDALONE IMPALA UDF DEVELOPMENT KIT.
// IT MUST BE BUILDABLE WITH C++98 AND WITHOUT ANY INTERNAL IMPALA HEADERS.
#include <assert.h>
#include <boost/cstdint.hpp>
#include <string.h>
// Only use noexcept if the compiler supports C++11 (some system compilers may not
// or may have it disabled by default).
#if __cplusplus >= 201103L
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif
// Macro to prepend to function definitions that will export the symbols to be visible
// for loading by Impala. It is recommended that UDFs be built with the compiler flags
// "-fvisibility=hidden -fvisibility-inlines-hidden" and only functions that are entry
// points for UDFs be exported with this macro.
#define IMPALA_UDF_EXPORT __attribute__ ((visibility ("default")))
/// This is the only Impala header required to develop UDFs and UDAs. This header
/// contains the types that need to be used and the FunctionContext object. The context
/// object serves as the interface object between the UDF/UDA and the impala process.
namespace impala {
class FunctionContextImpl;
}
namespace impala_udf {
/// All input and output values will be one of the structs below. The struct is a simple
/// object containing a boolean to store if the value is NULL and the value itself. The
/// value is unspecified if the NULL boolean is set.
struct AnyVal;
struct BooleanVal;
struct TinyIntVal;
struct SmallIntVal;
struct IntVal;
struct BigIntVal;
struct StringVal;
struct TimestampVal;
struct DateVal;
/// A FunctionContext is passed to every UDF/UDA and is the interface for the UDF to the
/// rest of the system. It contains APIs to examine the system state, report errors and
/// manage memory.
class FunctionContext {
public:
enum ImpalaVersion {
v1_2,
v1_3,
};
enum Type {
INVALID_TYPE = 0,
TYPE_NULL,
TYPE_BOOLEAN,
TYPE_TINYINT,
TYPE_SMALLINT,
TYPE_INT,
TYPE_BIGINT,
TYPE_FLOAT,
TYPE_DOUBLE,
TYPE_TIMESTAMP,
TYPE_STRING,
TYPE_DATE,
// Not used - maps to CHAR(N), which is not supported for UDFs and UDAs.
TYPE_FIXED_BUFFER,
TYPE_DECIMAL,
TYPE_VARCHAR,
// A fixed-size buffer, passed as a StringVal.
TYPE_FIXED_UDA_INTERMEDIATE
};
struct TypeDesc {
Type type;
/// Only valid if type == TYPE_DECIMAL
int precision;
int scale;
/// Only valid if type is one of TYPE_FIXED_BUFFER, TYPE_FIXED_UDA_INTERMEDIATE or
/// TYPE_VARCHAR.
int len;
};
struct UniqueId {
int64_t hi;
int64_t lo;
};
enum FunctionStateScope {
/// Indicates that the function state for this FunctionContext's UDF is shared across
/// the plan fragment (a query is divided into multiple plan fragments, each of which
/// is responsible for a part of the query execution). Within the plan fragment, there
/// may be multiple instances of the UDF executing concurrently with multiple
/// FunctionContexts sharing this state, meaning that the state must be
/// thread-safe. The Prepare() function for the UDF may be called with this scope
/// concurrently on a single host if the UDF will be evaluated in multiple plan
/// fragments on that host. In general, read-only state that doesn't need to be
/// recomputed for every UDF call should be fragment-local.
/// TODO: Move FRAGMENT_LOCAL states to query_state for multi-threading.
FRAGMENT_LOCAL,
/// Indicates that the function state is local to the execution thread. This state
/// does not need to be thread-safe. However, this state will be initialized (via the
/// Prepare() function) once for every execution thread, so fragment-local state
/// should be used when possible for better performance. In general, inexpensive
/// shared state that is written to by the UDF (e.g. scratch space) should be
/// thread-local.
THREAD_LOCAL,
};
/// Returns the version of Impala that's currently running.
ImpalaVersion version() const;
/// Returns the user that is running the query. Returns NULL if it is not
/// available.
const char* user() const;
/// Returns the effective user for authorization purposes. If a delegated user is
/// configured, returns that user, otherwise returns the same as user().
const char* effective_user() const;
/// Returns the query_id for the current query.
UniqueId query_id() const;
/// Sets an error for this UDF. If this is called, this will trigger the
/// query to fail.
void SetError(const char* error_msg);
/// Adds a warning that is returned to the user. This can include things like
/// overflow or other recoverable error conditions.
/// Warnings are capped at a maximum number. Returns true if the warning was
/// added and false if it was ignored due to the cap.
bool AddWarning(const char* warning_msg);
/// Returns true if there's been an error set.
bool has_error() const;
/// Returns the current error message. Returns NULL if there is no error.
const char* error_msg() const;
/// Allocates memory. All UDF/UDAs should use this if possible instead of malloc/new.
/// The UDF/UDA is responsible for calling Free() on all buffers returned by Allocate().
/// If Allocate() fails or causes the memory limit to be exceeded, the error will be
/// set in this object causing the query to fail.
/// TODO: 'byte_size' should be 64-bit. See IMPALA-2756.
uint8_t* Allocate(int byte_size) NOEXCEPT;
/// Wrapper around Allocate() to allocate a buffer of the given type "T".
template<typename T>
T* Allocate() {
return reinterpret_cast<T*>(Allocate(sizeof(T)));
}
/// Reallocates 'ptr' to the new byte_size. If the currently underlying allocation
/// is big enough, the original ptr will be returned. If the allocation needs to
/// grow, a new allocation is made that is at least 'byte_size' and the contents
/// of 'ptr' will be copied into it. If the new allocation fails or causes the
/// memory limit to be exceeded, the error will be set in this object.
///
/// This should be used for buffers that constantly get appended to.
/// TODO: 'byte_size' should be 64-bit. See IMPALA-2756.
uint8_t* Reallocate(uint8_t* ptr, int byte_size) NOEXCEPT;
/// Frees a buffer returned from Allocate() or Reallocate()
void Free(uint8_t* buffer) NOEXCEPT;
/// For allocations that cannot use the Allocate() API provided by this
/// object, TrackAllocation()/Free() can be used to just keep count of the
/// byte sizes. For each call to TrackAllocation(), the UDF/UDA must call
/// the corresponding Free().
void TrackAllocation(int64_t byte_size);
void Free(int64_t byte_size);
/// Methods for maintaining state across UDF/UDA function calls. SetFunctionState() can
/// be used to store a pointer that can then be retreived via GetFunctionState(). If
/// GetFunctionState() is called when no pointer is set, it will return
/// NULL. SetFunctionState() does not take ownership of 'ptr'; it is up to the UDF/UDA
/// to clean up any function state if necessary.
void SetFunctionState(FunctionStateScope scope, void* ptr);
void* GetFunctionState(FunctionStateScope scope) const;
/// Returns the return type information of this function. For UDAs, this is the final
/// return type of the UDA (e.g., the type returned by the finalize function).
const TypeDesc& GetReturnType() const;
/// Returns the intermediate type for UDAs, i.e., the one returned by
/// update and merge functions. Returns INVALID_TYPE for UDFs.
const TypeDesc& GetIntermediateType() const;
/// Returns the number of arguments to this function (not including the FunctionContext*
/// argument or the output of a UDA).
/// For UDAs, returns the number of logical arguments of the aggregate function, not
/// the number of arguments of the C++ function being executed.
int GetNumArgs() const;
/// Returns the type information for the arg_idx-th argument (0-indexed, not including
/// the FunctionContext* argument). Returns NULL if arg_idx is invalid.
/// For UDAs, returns the logical argument types of the aggregate function, not the
/// argument types of the C++ function being executed.
const TypeDesc* GetArgType(int arg_idx) const;
/// Returns true if the arg_idx-th input argument (indexed in the same way as
/// GetArgType()) is a constant (e.g. 5, "string", 1 + 1).
bool IsArgConstant(int arg_idx) const;
/// Returns a pointer to the value of the arg_idx-th input argument (indexed in the
/// same way as GetArgType()). Returns NULL if the argument is not constant. This
/// function can be used to obtain user-specified constants in a UDF's Init() or
/// Close() functions.
AnyVal* GetConstantArg(int arg_idx) const;
/// TODO: Do we need to add arbitrary key/value metadata. This would be plumbed
/// through the query. E.g. "select UDA(col, 'sample=true') from tbl".
/// const char* GetMetadata(const char*) const;
/// TODO: Add mechanism for UDAs to update stats similar to runtime profile counters
/// TODO: Add mechanism to query for table/column stats
/// Returns the underlying opaque implementation object. The UDF/UDA should not
/// use this. This is used internally.
impala::FunctionContextImpl* impl() const { return impl_; }
~FunctionContext();
private:
friend class impala::FunctionContextImpl;
FunctionContext();
/// Disable copy ctor and assignment operator
FunctionContext(const FunctionContext& other);
FunctionContext& operator=(const FunctionContext& other);
impala::FunctionContextImpl* impl_; // Owned by this object.
};
//----------------------------------------------------------------------------
//------------------------------- UDFs ---------------------------------------
//----------------------------------------------------------------------------
/// The UDF must implement this function prototype. This is not a typedef as the actual
/// UDF's signature varies from UDF to UDF.
/// typedef <*Val> Evaluate(FunctionContext* context, <const Val& arg>);
///
/// The UDF must return one of the *Val structs. The UDF must accept a pointer to a
/// FunctionContext object and then a const reference for each of the input arguments.
/// Examples of valid Udf signatures are:
/// 1) DoubleVal Example1(FunctionContext* context);
/// 2) IntVal Example2(FunctionContext* context, const IntVal& a1, const DoubleVal& a2);
///
/// UDFs can be variadic. The variable arguments must all come at the end and must be
/// the same type. A example signature is:
/// StringVal Concat(FunctionContext* context, const StringVal& separator,
/// int num_var_args, const StringVal* args);
/// In this case args[0] is the first variable argument and args[num_var_args - 1] is
/// the last.
///
/// ------- Memory Management -------
/// ---------------------------------
/// The UDF can assume that memory from input arguments will have the same lifetime as
/// results for the UDF. In other words, the UDF can return memory from input arguments
/// without making copies. For example, a function like substring will not need to
/// allocate and copy the smaller string.
///
/// Any state needed across calls must be stored and accessed via
/// FunctionContext::SetFunctionState() and FunctionContext::GetFunctionState(). The UDF
/// should not maintain any other state across calls since there is no guarantee on how
/// the execution is multithreaded or distributed.
///
/// For StringVal return values, the UDF can use StringVal(FunctionContext*, int)
/// ctor or the function StringVal::CopyFrom(FunctionContext*, const uint8_t*, size_t).
/// The memory consumed by the StringVal will be managed by Impala. Please see the UDA
/// section below for details.
///
/// -------- Execution Model --------
/// ---------------------------------
/// Execution model: For each UDF use occurring in a given query, at least one
/// FunctionContext will be created. For a given FunctionContext, the UDF's functions are
/// never called concurrently and therefore do not need to be thread-safe. State shared
/// across UDF invocations should be initialized and cleaned up using prepare and close
/// functions (described below).
///
/// Note that a single UDF use may produce multiple FunctionContexts for that UDF (this is
/// so the UDF can be executed concurrently in different threads). For example, the query
/// "select * from tbl where my_udf(x) > 0" may produce multiple FunctionContexts for
/// 'my_udf', each of which may concurrently be passed to 'my_udf's prepare, close, and
/// UDF functions.
///
/// --- Prepare / Close Functions ---
/// ---------------------------------
/// The UDF can optionally include a prepare function, specified in the "CREATE FUNCTION"
/// statement using "prepare_fn=<prepare function symbol>". The prepare function is called
/// before any calls to the UDF to evaluate values. This is the appropriate time for the
/// UDF to initialize any shared data structures, validate versions, etc. If there is an
/// error, this function should call FunctionContext::SetError()/
/// FunctionContext::AddWarning().
///
/// The prepare function is called multiple times with different FunctionStateScopes. It
/// will be called once per fragment with 'scope' set to FRAGMENT_LOCAL, and once per
/// execution thread with 'scope' set to THREAD_LOCAL.
typedef void (*UdfPrepare)(FunctionContext* context,
FunctionContext::FunctionStateScope scope);
/// The UDF can also optionally include a close function, specified in the "CREATE
/// FUNCTION" statement using "close_fn=<close function symbol>". The close function is
/// called after all calls to the UDF have completed. This is the appropriate time for the
/// UDF to deallocate any shared data structures that are not needed to maintain the
/// results. If there is an error, this function should call FunctionContext::SetError()/
/// FunctionContext::AddWarning().
//
/// The close function is called multiple times with different FunctionStateScopes. It
/// will be called once per fragment with 'scope' set to FRAGMENT_LOCAL, and once per
/// execution thread with 'scope' set to THREAD_LOCAL.
typedef void (*UdfClose)(FunctionContext* context,
FunctionContext::FunctionStateScope scope);
//----------------------------------------------------------------------------
//------------------------------- UDAs ---------------------------------------
//----------------------------------------------------------------------------
/// The UDA execution is broken up into a few steps. The general calling pattern
/// is one of these:
/// 1) Init(), Update() (repeatedly), Serialize()
/// 2) Init(), Update() (repeatedly), Finalize()
/// 3) Init(), Merge() (repeatedly), Serialize()
/// 4) Init(), Merge() (repeatedly), Finalize()
/// The UDA is registered with three types: the result type, the input type and
/// the intermediate type.
///
/// If the UDA needs a variable-sized buffer, it should use TYPE_STRING and allocate it
/// from the FunctionContext manually.
/// For UDAs that need a complex data structure as the intermediate state, the
/// intermediate type should be string and the UDA can cast the ptr to the structure
/// it is using.
///
/// Memory Management: allocations that are referred to by the intermediate values
/// returned by Init(), Update() and Merge() must be allocated via
/// FunctionContext::Allocate() and freed via FunctionContext::Free(). Both Serialize()
/// and Finalize() are responsible for cleaning up the intermediate value and freeing
/// such allocations. StringVals returned to Impala directly by Serialize(), Finalize()
/// or GetValue() should be backed by temporary results memory allocated via the
/// StringVal(FunctionContext*, int) ctor, StringVal::CopyFrom(FunctionContext*,
/// const uint8_t*, size_t), or StringVal::Resize().
///
/// Note that in the rare case the StringVal ctor or StringVal::CopyFrom() fail to
/// allocate memory, the StringVal object will be marked as a null string.
/// Serialize()/Finalize() should handle allocation failures by checking the is_null
/// field of the StringVal object and carry out appropriate error handling action.
/// Similarly, FunctionContext::Allocate()/Reallocate() may also fail to allocate
/// memory so callers should check the returned values before using them.
///
/// For clarity in documenting the UDA interface, the various types will be typedefed
/// here. The actual execution resolves all the types at runtime and none of these types
/// should actually be used.
///
/// TODO: add an Init() variant that takes the initial input value to avoid initializing
/// then immediately overwriting the value.
typedef AnyVal InputType;
typedef AnyVal InputType2;
typedef AnyVal ResultType;
typedef AnyVal IntermediateType;
/// UdaInit is called once for each aggregate group before calls to any of the
/// other functions below.
typedef void (*UdaInit)(FunctionContext* context, IntermediateType* result);
/// This is called for each input value. The UDA should update result based on the
/// input value. The update function can take any number of input arguments. Here
/// are some examples:
typedef void (*UdaUpdate)(FunctionContext* context, const InputType& input,
IntermediateType* result);
typedef void (*UdaUpdate2)(FunctionContext* context, const InputType& input,
const InputType2& input2, IntermediateType* result);
/// Merge an intermediate result 'src' into 'dst'.
typedef void (*UdaMerge)(FunctionContext* context, const IntermediateType& src,
IntermediateType* dst);
/// Serialize the intermediate type. The serialized data is then sent across the
/// wire.
/// No additional functions will be called with this FunctionContext object and the
/// UDA should do final clean (e.g. Free()) here.
typedef const IntermediateType (*UdaSerialize)(FunctionContext* context,
const IntermediateType& type);
/// Called once at the end to return the final value for this UDA.
/// No additional functions will be called with this FunctionContext object and the
/// UDA should do final clean (e.g. Free()) here.
typedef ResultType (*UdaFinalize)(FunctionContext* context, const IntermediateType& v);
//----------------------------------------------------------------------------
//-------------Implementation of the *Val structs ----------------------------
//----------------------------------------------------------------------------
struct AnyVal {
// Whether this value is NULL. If true, all other fields contain arbitrary values.
// UDF code should *not* assume that other fields of a NULL *Val struct have any
// particular value (e.g. 0 or -1).
bool is_null;
AnyVal(bool is_null = false) : is_null(is_null) {}
};
struct BooleanVal : public AnyVal {
bool val;
BooleanVal(bool val = false) : val(val) {}
static BooleanVal null() {
BooleanVal result;
result.is_null = true;
return result;
}
bool operator==(const BooleanVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const BooleanVal& other) const { return !(*this == other); }
};
struct TinyIntVal : public AnyVal {
typedef int8_t underlying_type_t;
underlying_type_t val;
TinyIntVal(underlying_type_t val = 0) : val(val) { }
static TinyIntVal null() {
TinyIntVal result;
result.is_null = true;
return result;
}
bool operator==(const TinyIntVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const TinyIntVal& other) const { return !(*this == other); }
};
struct SmallIntVal : public AnyVal {
typedef int16_t underlying_type_t;
underlying_type_t val;
SmallIntVal(underlying_type_t val = 0) : val(val) { }
static SmallIntVal null() {
SmallIntVal result;
result.is_null = true;
return result;
}
bool operator==(const SmallIntVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const SmallIntVal& other) const { return !(*this == other); }
};
struct IntVal : public AnyVal {
typedef int32_t underlying_type_t;
underlying_type_t val;
IntVal(underlying_type_t val = 0) : val(val) { }
static IntVal null() {
IntVal result;
result.is_null = true;
return result;
}
bool operator==(const IntVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const IntVal& other) const { return !(*this == other); }
};
struct BigIntVal : public AnyVal {
typedef int64_t underlying_type_t;
underlying_type_t val;
BigIntVal(underlying_type_t val = 0) : val(val) { }
static BigIntVal null() {
BigIntVal result;
result.is_null = true;
return result;
}
bool operator==(const BigIntVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const BigIntVal& other) const { return !(*this == other); }
};
struct FloatVal : public AnyVal {
float val;
FloatVal(float val = 0) : val(val) { }
static FloatVal null() {
FloatVal result;
result.is_null = true;
return result;
}
bool operator==(const FloatVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const FloatVal& other) const { return !(*this == other); }
};
struct DoubleVal : public AnyVal {
double val;
DoubleVal(double val = 0) : val(val) { }
static DoubleVal null() {
DoubleVal result;
result.is_null = true;
return result;
}
bool operator==(const DoubleVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const DoubleVal& other) const { return !(*this == other); }
};
/// This object has a compatible storage format with boost::ptime.
struct TimestampVal : public AnyVal {
/// Gregorian date. This has the same binary format as boost::gregorian::date.
int32_t date;
/// Nanoseconds in current day.
int64_t time_of_day;
TimestampVal(int32_t date = 0, int64_t time_of_day = 0) :
date(date), time_of_day(time_of_day) {
}
static TimestampVal null() {
TimestampVal result;
result.is_null = true;
return result;
}
bool operator==(const TimestampVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return date == other.date && time_of_day == other.time_of_day;
}
bool operator!=(const TimestampVal& other) const { return !(*this == other); }
};
/// Represents a DATE value.
/// - The minimum and maximum dates are 0001-01-01 and 9999-12-31. Valid dates must fall
/// in this range.
/// - Internally represents DATE values as number of days since 1970-01-01.
/// - This representation was chosen to be the same (bit-by-bit) as Parquet's date type.
/// (https://github.com/apache/parquet-format/blob/master/LogicalTypes.md#date)
/// - Proleptic Gregorian calendar is used to calculate the number of days since epoch,
/// which can lead to different representation of historical dates compared to Hive.
/// (https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar).
struct DateVal : public AnyVal {
typedef int32_t underlying_type_t;
underlying_type_t val;
explicit DateVal(underlying_type_t val = 0) : val(val) { }
static DateVal null() {
DateVal result;
result.is_null = true;
return result;
}
bool operator==(const DateVal& other) const {
if (is_null && other.is_null) return true;
if (is_null || other.is_null) return false;
return val == other.val;
}
bool operator!=(const DateVal& other) const { return !(*this == other); }
};
/// A String value represented as a buffer + length.
/// Note: there is a difference between a NULL string (is_null == true) and an
/// empty string (len == 0).
struct StringVal : public AnyVal {
// It's important to keep this as unsigned to avoid comparing with negative number
// in case of overflow.
static const unsigned MAX_LENGTH = (1 << 30);
// The length of the string buffer in bytes.
int len;
// Pointer to the start of the string buffer. The buffer is not aligned and is not
// null-terminated. Functions must not read or write past the end of the buffer.
// I.e. accessing ptr[i] where i >= len is invalid.
uint8_t* ptr;
/// Construct a StringVal from ptr/len. Note: this does not make a copy of ptr
/// so the buffer must exist as long as this StringVal does.
StringVal(uint8_t* ptr = NULL, int len = 0) : len(len), ptr(ptr) {
assert(len >= 0);
if (ptr == NULL) assert(len == 0);
}
/// Construct a StringVal from NULL-terminated c-string. Note: this does not make a
/// copy of ptr so the underlying string must exist as long as this StringVal does.
StringVal(const char* ptr)
: len(strlen(ptr)),
ptr(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(ptr))) {}
/// Creates a StringVal, allocating a new buffer with 'len'. This should
/// be used to return StringVal objects in UDF/UDAs that need to allocate new
/// string memory.
///
/// If the memory allocation fails, e.g. because the intermediate value would be too
/// large, the constructor will construct a NULL string and set an error on the function
/// context.
///
/// The memory backing this StringVal is managed by the Impala runtime and so doesn't need
/// to be explicitly freed.
StringVal(FunctionContext* context, int len) NOEXCEPT;
/// Resize a string value to 'len'. If 'len' is the same as or smaller than the current
/// length, truncates the string. Otherwise, increases the string's length, allocating
/// new memory and copying over the current contents if needed. The content of the new
/// space is undefined. If a resize fails, the length and contents of the StringVal are
/// unchanged.
///
/// Resized strings can be returned from UDFs as the result value. Callers do not
/// otherwise need to be concerned with backing storage, which is managed by the
/// Impala runtime and freed at some point after the UDF returns.
///
/// Returns true on success, false on failure.
bool Resize(FunctionContext* context, int len) NOEXCEPT;
/// Will create a new StringVal with the given dimension and copy the data from the
/// parameters. In case of an error will return a NULL string and set an error on the
/// function context.
///
/// Note that the memory for the buffer of the new StringVal is managed by Impala.
/// Impala will handle freeing it. Callers should not call Free() on the 'ptr' of
/// the StringVal returned.
static StringVal CopyFrom(FunctionContext* ctx, const uint8_t* buf, size_t len)
NOEXCEPT;
static StringVal null() {
StringVal sv;
sv.is_null = true;
return sv;
}
bool operator==(const StringVal& other) const {
if (is_null != other.is_null) return false;
if (is_null) return true;
if (len != other.len) return false;
return ptr == other.ptr || memcmp(ptr, other.ptr, len) == 0;
}
bool operator!=(const StringVal& other) const { return !(*this == other); }
};
struct DecimalVal : public impala_udf::AnyVal {
/// Decimal data is stored as an unscaled integer value. For example, the decimal 1.00
/// (precision 3, scale 2) is stored as 100. The byte size necessary to store the
/// decimal depends on the precision, which determines which field of the union should
/// be used to store and manipulate the unscaled value.
///
/// precision between 0-9: val4 (4 bytes)
/// precision between 10-18: val8 (8 bytes)
/// precision between 19-38: val16 (16 bytes)
///
/// While it is always safe to use a larger field than necessary, it may result in worse
/// performance. For example, a UDF that only uses val16 can handle any precision but
/// may be slower than one that uses val4 or val8. This is because the least-significant
/// bits of all three union fields are the same (assuming a little-endian architecture).
union {
int32_t val4;
int64_t val8;
__int128_t val16;
};
DecimalVal() : val16(0) {}
DecimalVal(int32_t v) : val16(v) {}
DecimalVal(int64_t v) : val16(v) {}
DecimalVal(__int128_t v) : val16(v) {}
static DecimalVal null() {
DecimalVal result;
result.is_null = true;
return result;
}
DecimalVal& operator=(const DecimalVal& other) {
// Depending on the compiler, the default assignment operator may require 16-byte
// alignment of 'this' and 'other'. Cast to void* so the compiler doesn't change back
// to an assignment.
memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(&other),
sizeof(DecimalVal));
return *this;
}
DecimalVal(const DecimalVal& other) {
*this = other;
}
};
typedef uint8_t* BufferVal;
}
#endif