blob: 69dfb50864199ea1aa03b5bfb667a66972af85c6 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <math.h>
#include <time.h>
#include <limits>
#include <map>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/unordered_map.hpp>
#include "codegen/llvm-codegen.h"
#include "common/init.h"
#include "common/object-pool.h"
#include "exprs/anyval-util.h"
#include "exprs/is-null-predicate.h"
#include "exprs/like-predicate.h"
#include "exprs/literal.h"
#include "exprs/null-literal.h"
#include "exprs/scalar-expr-evaluator.h"
#include "exprs/scalar-expr.h"
#include "exprs/string-functions.h"
#include "exprs/timestamp-functions.h"
#include "exprs/timezone_db.h"
#include "gen-cpp/Exprs_types.h"
#include "gen-cpp/ImpalaInternalService_types.h"
#include "gen-cpp/hive_metastore_types.h"
#include "rpc/thrift-client.h"
#include "rpc/thrift-server.h"
#include "runtime/date-value.h"
#include "runtime/mem-pool.h"
#include "runtime/mem-tracker.h"
#include "runtime/raw-value.inline.h"
#include "runtime/runtime-state.h"
#include "runtime/string-value.h"
#include "runtime/timestamp-parse-util.h"
#include "runtime/timestamp-value.h"
#include "runtime/timestamp-value.inline.h"
#include "service/fe-support.h"
#include "service/impala-server.h"
#include "statestore/statestore.h"
#include "testutil/gtest-util.h"
#include "testutil/impalad-query-executor.h"
#include "testutil/in-process-servers.h"
#include "udf/udf-test-harness.h"
#include "util/asan.h"
#include "util/debug-util.h"
#include "util/metrics.h"
#include "util/string-parser.h"
#include "util/string-util.h"
#include "util/test-info.h"
#include "utility-functions.h"
#include "common/names.h"
DECLARE_bool(abort_on_config_error);
DECLARE_bool(disable_optimization_passes);
DECLARE_bool(use_utc_for_unix_timestamp_conversions);
DECLARE_string(hdfs_zone_info_zip);
namespace posix_time = boost::posix_time;
using boost::bad_lexical_cast;
using boost::date_time::c_local_adjustor;
using boost::posix_time::from_time_t;
using boost::posix_time::ptime;
using boost::posix_time::to_tm;
using std::numeric_limits;
using namespace Apache::Hadoop::Hive;
using namespace impala;
namespace impala {
// America/Anguilla timezone does not observe DST.
// Use this timezone in tests where DST changes may cause problems.
const char* TEST_TZ_WITHOUT_DST = "America/Anguilla";
ImpaladQueryExecutor* executor_;
scoped_ptr<MetricGroup> statestore_metrics(new MetricGroup("statestore_metrics"));
Statestore* statestore;
template <typename ORIGINAL_TYPE, typename VAL_TYPE>
string LiteralToString(VAL_TYPE val) {
return lexical_cast<string>(val);
}
template<>
string LiteralToString<float, float>(float val) {
stringstream ss;
ss << "cast("
<< lexical_cast<string>(val)
<< " as float)";
return ss.str();
}
template<>
string LiteralToString<float, double>(double val) {
stringstream ss;
ss << "cast("
<< lexical_cast<string>(val)
<< " as float)";
return ss.str();
}
template<>
string LiteralToString<double, double>(double val) {
stringstream ss;
ss << "cast("
<< lexical_cast<string>(val)
<< " as double)";
return ss.str();
}
// For writing C++ std::strings as impala literals, we have to ensure that characters like
// single-quote are escaped properly. To do this, we escape every character into its octal
// equivalent: \PQR for some octal digits P, Q, and R. Currently, this only works for
// ASCII literals.
string StringToOctalLiteral(const string& s) {
string result(4 * s.size(), 0);
for (int i = 0; i < s.size(); ++i) {
result[4 * i] = '\\';
result[4 * i + 1] = '0' + (s[i] / 64);
result[4 * i + 2] = '0' + ((s[i] / 8) % 8);
result[4 * i + 3] = '0' + (s[i] % 8);
}
return result;
}
// Override the time zone for the duration of the scope. The time zone is overridden
// using an environment variable there is no risk of making a permanent system change
// and no special permissions are needed. This is not thread-safe.
class ScopedTimeZoneOverride {
public:
ScopedTimeZoneOverride(const char* tz_name)
: original_tz_name_(getenv("TZ")),
new_tz_name_(tz_name),
new_tz_(TimezoneDatabase::FindTimezone(new_tz_name_)) {
EXPECT_NE(nullptr, new_tz_) << "Could not find " << new_tz_name_ << " time zone.";
setenv("TZ", new_tz_name_, true);
tzset();
}
~ScopedTimeZoneOverride() {
if (original_tz_name_ == nullptr) {
unsetenv("TZ");
} else {
setenv("TZ", original_tz_name_, true);
}
tzset();
}
const Timezone& GetTimezone() const { return *new_tz_; }
private:
const char* const original_tz_name_;
const char* const new_tz_name_;
const Timezone* new_tz_;
};
// Enable FLAGS_use_local_tz_for_unix_timestamp_conversions for the duration of the scope.
class ScopedLocalUnixTimestampConversionOverride {
bool original_;
public:
ScopedLocalUnixTimestampConversionOverride() {
original_ = FLAGS_use_local_tz_for_unix_timestamp_conversions;
FLAGS_use_local_tz_for_unix_timestamp_conversions = true;
}
~ScopedLocalUnixTimestampConversionOverride() {
FLAGS_use_local_tz_for_unix_timestamp_conversions = original_;
}
};
class ExprTest : public testing::TestWithParam<std::tuple<bool, bool>> {
public:
// Run once (independent of parameter values).
static void SetUpTestCase() {
InitFeSupport(false);
ABORT_IF_ERROR(impala::LlvmCodeGen::InitializeLlvm());
// The host running this test might have an out-of-date tzdata package installed.
// To avoid tzdata related issues, we will load time-zone db from the testdata
// directory.
FLAGS_hdfs_zone_info_zip = Substitute("file://$0/testdata/tzdb/2017c.zip",
getenv("IMPALA_HOME"));
ABORT_IF_ERROR(TimezoneDatabase::Initialize());
// Disable llvm optimization passes if the env var is no set to true. Running without
// the optimizations makes the tests run much faster.
char* optimizations = getenv("EXPR_TEST_ENABLE_OPTIMIZATIONS");
if (optimizations != NULL && strcmp(optimizations, "true") == 0) {
cout << "Running with optimization passes." << endl;
FLAGS_disable_optimization_passes = false;
} else {
cout << "Running without optimization passes." << endl;
FLAGS_disable_optimization_passes = true;
}
// Create an in-process Impala server and in-process backends for test environment
// without any startup validation check
FLAGS_abort_on_config_error = false;
VLOG_CONNECTION << "creating test env";
VLOG_CONNECTION << "starting backends";
statestore = new Statestore(statestore_metrics.get());
IGNORE_LEAKING_OBJECT(statestore);
// Pass in 0 to have the statestore use an ephemeral port for the service.
ABORT_IF_ERROR(statestore->Init(0));
InProcessImpalaServer* impala_server;
ABORT_IF_ERROR(InProcessImpalaServer::StartWithEphemeralPorts(
FLAGS_hostname, statestore->port(), &impala_server));
IGNORE_LEAKING_OBJECT(impala_server);
executor_ = new ImpaladQueryExecutor(FLAGS_hostname, impala_server->GetBeeswaxPort());
ABORT_IF_ERROR(executor_->Setup());
}
protected:
// Pool for objects to be destroyed during test teardown.
ObjectPool pool_;
// Maps from enum value of primitive integer type to the minimum value that is
// outside of the next smaller-resolution type. For example the value for type
// TYPE_SMALLINT is numeric_limits<int8_t>::max()+1. There is a GREATEST test in
// the MathFunctions tests that requires this to be an ordered map.
typedef map<int, int64_t> IntValMap;
IntValMap min_int_values_;
// Maps from primitive float type to smallest positive value that is larger
// than the largest value of the next smaller-resolution type.
typedef unordered_map<int, double> FloatValMap;
FloatValMap min_float_values_;
// Maps from enum value of primitive type to a string representation of a default
// value for testing. For int and float types the strings represent the corresponding
// min values (in the maps above). For non-numeric types the default values are listed
// below.
unordered_map<int, string> default_type_strs_;
string default_bool_str_;
string default_string_str_;
string default_timestamp_str_;
string default_decimal_str_;
string default_date_str_;
// Corresponding default values.
bool default_bool_val_;
string default_string_val_;
TimestampValue default_timestamp_val_;
DateValue default_date_val_;
bool disable_codegen_;
bool enable_expr_rewrites_;
virtual void SetUp() {
disable_codegen_ = std::get<0>(GetParam());
enable_expr_rewrites_ = std::get<1>(GetParam());
LOG(INFO) << Substitute(
"Test case: disable_codegen=$0 enable_expr_rewrites=$1",
disable_codegen_, enable_expr_rewrites_);
executor_->ClearExecOptions();
executor_->PushExecOption(Substitute("DISABLE_CODEGEN=$0",
disable_codegen_ ? 1 : 0));
executor_->PushExecOption(Substitute("ENABLE_EXPR_REWRITES=$0",
enable_expr_rewrites_ ? 1 : 0));
// The following have no effect when codegen is disabled, but don't
// harm anything either. They generally prevent the planner from doing
// anything clever here.
executor_->PushExecOption("EXEC_SINGLE_NODE_ROWS_THRESHOLD=0");
executor_->PushExecOption("DISABLE_CODEGEN_ROWS_THRESHOLD=0");
// Some tests select rows that take a long time to materialize (e.g.
// "select length(unhex(repeat('a', 1024 * 1024 * 1024)))") so set the client fetch
// timeout to a high value.
executor_->PushExecOption("FETCH_ROWS_TIMEOUT_MS=100000");
min_int_values_[TYPE_TINYINT] = 1;
min_int_values_[TYPE_SMALLINT] =
static_cast<int64_t>(numeric_limits<int8_t>::max()) + 1;
min_int_values_[TYPE_INT] = static_cast<int64_t>(numeric_limits<int16_t>::max()) + 1;
min_int_values_[TYPE_BIGINT] =
static_cast<int64_t>(numeric_limits<int32_t>::max()) + 1;
min_float_values_[TYPE_FLOAT] = 1.1;
min_float_values_[TYPE_DOUBLE] =
static_cast<double>(numeric_limits<float>::max()) + 1.1;
// Set up default test types, values, and strings.
default_bool_str_ = "false";
default_string_str_ = "'abc'";
default_timestamp_str_ = "cast('2011-01-01 09:01:01' as timestamp)";
default_decimal_str_ = "1.23";
default_date_str_ = "cast('2011-01-01' as date)";
default_bool_val_ = false;
default_string_val_ = "abc";
default_timestamp_val_ = TimestampValue::FromUnixTime(1293872461,
TimezoneDatabase::GetUtcTimezone());
default_date_val_ = DateValue(2011, 1, 1);
default_type_strs_[TYPE_TINYINT] =
lexical_cast<string>(min_int_values_[TYPE_TINYINT]);
default_type_strs_[TYPE_SMALLINT] =
lexical_cast<string>(min_int_values_[TYPE_SMALLINT]);
default_type_strs_[TYPE_INT] =
lexical_cast<string>(min_int_values_[TYPE_INT]);
default_type_strs_[TYPE_BIGINT] =
lexical_cast<string>(min_int_values_[TYPE_BIGINT]);
// Don't use lexical cast here because it results
// in a string 1.1000000000000001 that messes up the tests.
stringstream ss;
ss << "cast("
<< lexical_cast<string>(min_float_values_[TYPE_FLOAT]) << " as float)";
default_type_strs_[TYPE_FLOAT] = ss.str();
ss.str("");
ss << "cast("
<< lexical_cast<string>(min_float_values_[TYPE_FLOAT]) << " as double)";
default_type_strs_[TYPE_DOUBLE] = ss.str();
default_type_strs_[TYPE_BOOLEAN] = default_bool_str_;
default_type_strs_[TYPE_STRING] = default_string_str_;
default_type_strs_[TYPE_TIMESTAMP] = default_timestamp_str_;
default_type_strs_[TYPE_DECIMAL] = default_decimal_str_;
default_type_strs_[TYPE_DATE] = default_date_str_;
}
virtual void TearDown() { pool_.Clear(); }
string GetValue(const string& expr, const ColumnType& expr_type,
bool expect_error = false) {
string stmt = "select " + expr;
vector<FieldSchema> result_types;
Status status = executor_->Exec(stmt, &result_types);
if (!status.ok()) {
EXPECT_TRUE(expect_error) << "stmt: " << stmt << "\nerror: " << status.GetDetail();
return "";
}
string result_row;
status = executor_->FetchResult(&result_row);
if (expect_error) {
EXPECT_FALSE(status.ok()) << "Expected error\nstmt: " << stmt;
return "";
}
EXPECT_TRUE(status.ok()) << "stmt: " << stmt << "\nerror: " << status.GetDetail();
EXPECT_EQ(TypeToOdbcString(expr_type.type), result_types[0].type) << expr;
return result_row;
}
template <typename T>
T ConvertValue(const string& value);
void TestStringValue(const string& expr, const string& expected_result) {
EXPECT_EQ(expected_result, GetValue(expr, TYPE_STRING)) << expr;
}
// Tests that DST of the given timezone ends at 3am
void TestAusDSTEndingForEastTimeZone(const string& time_zone);
void TestAusDSTEndingForCentralTimeZone(const string& time_zone);
void TestCharValue(const string& expr, const string& expected_result,
const ColumnType& type) {
EXPECT_EQ(expected_result, GetValue(expr, type)) << expr;
}
string TestStringValueRegex(const string& expr, const string& regex) {
const string results = GetValue(expr, TYPE_STRING);
static const boost::regex e(regex);
const bool is_regex_match = regex_match(results, e);
EXPECT_TRUE(is_regex_match);
return results;
}
void TestLastDayFunction() {
// Test common months (with and without time component).
TestTimestampValue("last_day('2003-01-02 04:24:04.1579')",
TimestampValue::ParseSimpleDateFormat("2003-01-31 00:00:00", 19));
TestTimestampValue("last_day('2003-02-02')",
TimestampValue::ParseSimpleDateFormat("2003-02-28 00:00:00"));
TestTimestampValue("last_day('2003-03-02 03:21:12.0058')",
TimestampValue::ParseSimpleDateFormat("2003-03-31 00:00:00"));
TestTimestampValue("last_day('2003-04-02')",
TimestampValue::ParseSimpleDateFormat("2003-04-30 00:00:00"));
TestTimestampValue("last_day('2003-05-02')",
TimestampValue::ParseSimpleDateFormat("2003-05-31 00:00:00"));
TestTimestampValue("last_day('2003-06-02')",
TimestampValue::ParseSimpleDateFormat("2003-06-30 00:00:00"));
TestTimestampValue("last_day('2003-07-02 00:01:01.125')",
TimestampValue::ParseSimpleDateFormat("2003-07-31 00:00:00"));
TestTimestampValue("last_day('2003-08-02')",
TimestampValue::ParseSimpleDateFormat("2003-08-31 00:00:00"));
TestTimestampValue("last_day('2003-09-02')",
TimestampValue::ParseSimpleDateFormat("2003-09-30 00:00:00"));
TestTimestampValue("last_day('2003-10-02')",
TimestampValue::ParseSimpleDateFormat("2003-10-31 00:00:00"));
TestTimestampValue("last_day('2003-11-02 12:30:16')",
TimestampValue::ParseSimpleDateFormat("2003-11-30 00:00:00"));
TestTimestampValue("last_day('2003-12-02')",
TimestampValue::ParseSimpleDateFormat("2003-12-31 00:00:00"));
// Test leap years and special cases.
TestTimestampValue("last_day('2004-02-13')",
TimestampValue::ParseSimpleDateFormat("2004-02-29 00:00:00"));
TestTimestampValue("last_day('2008-02-13')",
TimestampValue::ParseSimpleDateFormat("2008-02-29 00:00:00"));
TestTimestampValue("last_day('2000-02-13')",
TimestampValue::ParseSimpleDateFormat("2000-02-29 00:00:00"));
TestTimestampValue("last_day('1900-02-13')",
TimestampValue::ParseSimpleDateFormat("1900-02-28 00:00:00"));
TestTimestampValue("last_day('2100-02-13')",
TimestampValue::ParseSimpleDateFormat("2100-02-28 00:00:00"));
// Test corner cases.
TestTimestampValue("last_day('1400-01-01 00:00:00')",
TimestampValue::ParseSimpleDateFormat("1400-01-31 00:00:00"));
TestTimestampValue("last_day('9999-12-31 23:59:59')",
TimestampValue::ParseSimpleDateFormat("9999-12-31 00:00:00"));
// Test invalid input.
TestIsNull("last_day('12202010')", TYPE_TIMESTAMP);
TestIsNull("last_day('')", TYPE_TIMESTAMP);
TestIsNull("last_day(NULL)", TYPE_TIMESTAMP);
TestIsNull("last_day('02-13-2014')", TYPE_TIMESTAMP);
TestIsNull("last_day('00:00:00')", TYPE_TIMESTAMP);
}
void TestNextDayFunction() {
// Sequential test cases
TestTimestampValue("next_day('2016-05-01','Sunday')",
TimestampValue::ParseSimpleDateFormat("2016-05-08 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Monday')",
TimestampValue::ParseSimpleDateFormat("2016-05-02 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Tuesday')",
TimestampValue::ParseSimpleDateFormat("2016-05-03 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Wednesday')",
TimestampValue::ParseSimpleDateFormat("2016-05-04 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Thursday')",
TimestampValue::ParseSimpleDateFormat("2016-05-05 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Friday')",
TimestampValue::ParseSimpleDateFormat("2016-05-06 00:00:00", 19));
TestTimestampValue("next_day('2016-05-01','Saturday')",
TimestampValue::ParseSimpleDateFormat("2016-05-07 00:00:00", 19));
// Random test cases
TestTimestampValue("next_day('1910-01-18','SunDay')",
TimestampValue::ParseSimpleDateFormat("1910-01-23 00:00:00", 19));
TestTimestampValue("next_day('1916-06-05', 'SUN')",
TimestampValue::ParseSimpleDateFormat("1916-06-11 00:00:00", 19));
TestTimestampValue("next_day('1932-11-08','monday')",
TimestampValue::ParseSimpleDateFormat("1932-11-14 00:00:00", 19));
TestTimestampValue("next_day('1933-09-11','Mon')",
TimestampValue::ParseSimpleDateFormat("1933-09-18 00:00:00", 19));
TestTimestampValue("next_day('1934-03-21','TUeSday')",
TimestampValue::ParseSimpleDateFormat("1934-03-27 00:00:00", 19));
TestTimestampValue("next_day('1954-02-25','tuE')",
TimestampValue::ParseSimpleDateFormat("1954-03-02 00:00:00", 19));
TestTimestampValue("next_day('1965-04-18','WeDneSdaY')",
TimestampValue::ParseSimpleDateFormat("1965-04-21 00:00:00", 19));
TestTimestampValue("next_day('1966-08-29','wed')",
TimestampValue::ParseSimpleDateFormat("1966-08-31 00:00:00", 19));
TestTimestampValue("next_day('1968-07-23','tHurSday')",
TimestampValue::ParseSimpleDateFormat("1968-07-25 00:00:00", 19));
TestTimestampValue("next_day('1969-05-28','thu')",
TimestampValue::ParseSimpleDateFormat("1969-05-29 00:00:00", 19));
TestTimestampValue("next_day('1989-10-12','fRIDay')",
TimestampValue::ParseSimpleDateFormat("1989-10-13 00:00:00", 19));
TestTimestampValue("next_day('1973-10-02','frI')",
TimestampValue::ParseSimpleDateFormat("1973-10-05 00:00:00", 19));
TestTimestampValue("next_day('2000-02-29','saTUrDaY')",
TimestampValue::ParseSimpleDateFormat("2000-03-04 00:00:00", 19));
TestTimestampValue("next_day('2013-04-12','sat')",
TimestampValue::ParseSimpleDateFormat("2013-04-13 00:00:00", 19));
TestTimestampValue("next_day('2013-12-25','Saturday')",
TimestampValue::ParseSimpleDateFormat("2013-12-28 00:00:00", 19));
// Explicit timestamp conversion tests
TestTimestampValue("next_day(to_timestamp('12-27-2008', 'MM-dd-yyyy'), 'moN')",
TimestampValue::ParseSimpleDateFormat("2008-12-29 00:00:00", 19));
TestTimestampValue("next_day(to_timestamp('2007-20-10 11:22', 'yyyy-dd-MM HH:mm'),\
'TUeSdaY')", TimestampValue::ParseSimpleDateFormat("2007-10-23 11:22:00", 19));
TestTimestampValue("next_day(to_timestamp('18-11-2070 09:12', 'dd-MM-yyyy HH:mm'),\
'WeDneSdaY')", TimestampValue::ParseSimpleDateFormat("2070-11-19 09:12:00", 19));
TestTimestampValue("next_day(to_timestamp('12-1900-05', 'dd-yyyy-MM'), 'tHurSday')",
TimestampValue::ParseSimpleDateFormat("1900-05-17 00:00:00", 19));
TestTimestampValue("next_day(to_timestamp('08-1987-21', 'MM-yyyy-dd'), 'FRIDAY')",
TimestampValue::ParseSimpleDateFormat("1987-08-28 00:00:00", 19));
TestTimestampValue("next_day(to_timestamp('02-04-2001', 'dd-MM-yyyy'), 'SAT')",
TimestampValue::ParseSimpleDateFormat("2001-04-07 00:00:00", 19));
TestTimestampValue("next_day(to_timestamp('1970-01-31 00:00:00',\
'yyyy-MM-dd HH:mm:ss'), 'SunDay')",
TimestampValue::ParseSimpleDateFormat("1970-02-01 00:00:00", 19));
// Invalid input: unacceptable date parameter
TestIsNull("next_day('12202010','Saturday')", TYPE_TIMESTAMP);
TestIsNull("next_day('2011 02 11','thu')", TYPE_TIMESTAMP);
TestIsNull("next_day('09-19-2012xyz','monDay')", TYPE_TIMESTAMP);
TestIsNull("next_day('000000000000000','wed')", TYPE_TIMESTAMP);
TestIsNull("next_day('hell world!','fRiDaY')", TYPE_TIMESTAMP);
TestIsNull("next_day('t1c7t0c9','sunDAY')", TYPE_TIMESTAMP);
TestIsNull("next_day(NULL ,'sunDAY')", TYPE_TIMESTAMP);
// Invalid input: wrong weekday parameter
for (const string& day: { "s", "SA", "satu", "not-a-day" }) {
const string expr = "next_day('2013-12-25','" + day + "')";
TestError(expr);
}
TestError("next_day('2013-12-25', NULL)");
TestError("next_day(NULL, NULL)");
}
// This macro adds a scoped trace to provide the line number of the caller upon failure.
#define EXPECT_BETWEEN(start, value, end) { \
SCOPED_TRACE(""); \
ExpectBetween(start, value, end); \
}
template <typename T>
void ExpectBetween(T start, T value, T end) {
EXPECT_LE(start, value);
EXPECT_LE(value, end);
}
// Test conversions of Timestamps to and from string/int with values related to the
// Unix epoch. The caller should set the current time zone before calling.
// 'unix_time_at_local_epoch' should be the expected value of the Unix time when it
// was 1970-01-01 in the current time zone. 'local_time_at_unix_epoch' should be the
// local time at the Unix epoch (1970-01-01 UTC).
void TestTimestampUnixEpochConversions(int64_t unix_time_at_local_epoch,
string local_time_at_unix_epoch) {
TestValue("unix_timestamp(cast('" + local_time_at_unix_epoch + "' as timestamp))",
TYPE_BIGINT, 0);
TestValue("unix_timestamp('" + local_time_at_unix_epoch + "')", TYPE_BIGINT, 0);
TestValue("unix_timestamp('" + local_time_at_unix_epoch +
"', 'yyyy-MM-dd HH:mm:ss')", TYPE_BIGINT, 0);
TestValue("unix_timestamp('1970-01-01', 'yyyy-MM-dd')", TYPE_BIGINT,
unix_time_at_local_epoch);
TestValue("unix_timestamp('1970-01-01 10:10:10', 'yyyy-MM-dd')", TYPE_BIGINT,
unix_time_at_local_epoch);
TestValue("unix_timestamp('" + local_time_at_unix_epoch
+ " extra text', 'yyyy-MM-dd HH:mm:ss')", TYPE_BIGINT, 0);
TestStringValue("cast(cast(0 as timestamp) as string)", local_time_at_unix_epoch);
TestStringValue("cast(cast(0 as timestamp) as string)", local_time_at_unix_epoch);
TestStringValue("from_unixtime(0)", local_time_at_unix_epoch);
TestStringValue("from_unixtime(cast(0 as bigint))", local_time_at_unix_epoch);
TestIsNull("from_unixtime(NULL)", TYPE_STRING);
TestStringValue("from_unixtime(0, 'yyyy-MM-dd HH:mm:ss')",
local_time_at_unix_epoch);
TestStringValue("from_unixtime(cast(0 as bigint), 'yyyy-MM-dd HH:mm:ss')",
local_time_at_unix_epoch);
TestStringValue("from_unixtime(" + lexical_cast<string>(unix_time_at_local_epoch)
+ ", 'yyyy-MM-dd')", "1970-01-01");
TestStringValue("from_unixtime(cast(" + lexical_cast<string>(unix_time_at_local_epoch)
+ " as bigint), 'yyyy-MM-dd')", "1970-01-01");
TestIsNull("to_timestamp(NULL)", TYPE_TIMESTAMP);
TestIsNull("to_timestamp(NULL, 'yyyy-MM-dd')", TYPE_TIMESTAMP);
TestIsNull("from_timestamp(NULL, 'yyyy-MM-dd')", TYPE_STRING);
TestStringValue("cast(to_timestamp(" + lexical_cast<string>(unix_time_at_local_epoch)
+ ") as string)", "1970-01-01 00:00:00");
TestStringValue("cast(to_timestamp('1970-01-01 00:00:00', 'yyyy-MM-dd HH:mm:ss') \
as string)", "1970-01-01 00:00:00");
TestStringValue("from_timestamp('1970-01-01 00:00:00', 'yyyy-MM-dd HH:mm:ss')",
"1970-01-01 00:00:00");
}
// Verify that output of 'query' has the same precision and scale as 'expected_type'.
// 'query' is an expression, optionally followed by a from clause which is needed
// for testing aggregate expressions.
void TestDecimalResultType(const string& query, const ColumnType& expected_type) {
// For the case with from clause, we need to generate the "typeof query" by first
// extracting the select list.
size_t from_offset = query.find("from");
string typeof_query;
if (from_offset != string::npos) {
int query_len = query.length();
typeof_query = "typeof(" + query.substr(0, from_offset) + ")" +
query.substr(from_offset, query_len - from_offset);
} else {
typeof_query = "typeof(" + query + ")";
}
const string typeof_result = GetValue(typeof_query, TYPE_STRING);
EXPECT_EQ(expected_type.DebugString(), typeof_result) << typeof_query;
}
// Decimals don't work with TestValue.
// TODO: figure out what operators need to be implemented to work with EXPECT_EQ
template<typename T>
void TestDecimalValue(const string& query, const T& expected_result,
const ColumnType& expected_type) {
// Verify precision and scale of the expression match the expected type.
TestDecimalResultType(query, expected_type);
// Verify the expression result matches the expected result, for the given the
// precision and scale.
const string value = GetValue(query, expected_type);
StringParser::ParseResult result;
// These require that we've passed the correct type to StringToDecimal(), so these
// results are valid only when TestDecimalResultType() succeeded.
switch (expected_type.GetByteSize()) {
case 4:
EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int32_t>(
value.data(), value.size(), expected_type, false, &result).value()) << query;
break;
case 8:
EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int64_t>(
value.data(), value.size(), expected_type, false, &result).value()) << query;
break;
case 16:
EXPECT_EQ(expected_result.value(), StringParser::StringToDecimal<int128_t>(
value.data(), value.size(), expected_type, false, &result).value()) << query;
break;
default:
EXPECT_TRUE(false) << expected_type << " " << expected_type.GetByteSize();
}
EXPECT_EQ(result, StringParser::PARSE_SUCCESS);
}
template <class T> void TestValue(const string& expr, const ColumnType& expr_type,
const T& expected_result) {
const string result = GetValue(expr, expr_type);
switch (expr_type.type) {
case TYPE_BOOLEAN:
EXPECT_EQ(expected_result, ConvertValue<bool>(result)) << expr;
break;
case TYPE_TINYINT:
EXPECT_EQ(expected_result, ConvertValue<int8_t>(result)) << expr;
break;
case TYPE_SMALLINT:
EXPECT_EQ(expected_result, ConvertValue<int16_t>(result)) << expr;
break;
case TYPE_INT:
EXPECT_EQ(expected_result, ConvertValue<int32_t>(result)) << expr;
break;
case TYPE_BIGINT:
EXPECT_EQ(expected_result, ConvertValue<int64_t>(result)) << expr;
break;
case TYPE_FLOAT: {
// Converting the float back from a string is inaccurate so convert
// the expected result to a string.
// In case the expected_result was passed in as an int or double, convert it.
string expected_str;
float expected_float;
expected_float = static_cast<float>(expected_result);
RawValue::PrintValue(reinterpret_cast<const void*>(&expected_float),
TYPE_FLOAT, -1, &expected_str);
EXPECT_EQ(expected_str, result) << expr;
break;
}
case TYPE_DOUBLE: {
string expected_str;
double expected_double;
expected_double = static_cast<double>(expected_result);
RawValue::PrintValue(reinterpret_cast<const void*>(&expected_double),
TYPE_DOUBLE, -1, &expected_str);
EXPECT_EQ(expected_str, result) << expr;
break;
}
default:
ASSERT_TRUE(false) << "invalid TestValue() type: " << expr_type;
}
}
void TestIsNull(const string& expr, const ColumnType& expr_type) {
EXPECT_TRUE(GetValue(expr, expr_type) == "NULL") << expr;
}
void TestIsNotNull(const string& expr, const ColumnType& expr_type) {
EXPECT_TRUE(GetValue(expr, expr_type) != "NULL") << expr;
}
void TestError(const string& expr) {
GetValue(expr, INVALID_TYPE, /* expect_error */ true);
}
void TestNonOkStatus(const string& expr) {
string stmt = "select " + expr;
vector<FieldSchema> result_types;
Status status = executor_->Exec(stmt, &result_types);
ASSERT_FALSE(status.ok()) << "stmt: " << stmt << "\nunexpected Status::OK.";
}
// "Execute 'expr' and check that the returned error ends with 'error_string'"
void TestErrorString(const string& expr, const string& error_string) {
string stmt = "select " + expr;
vector<FieldSchema> result_types;
string result_row;
Status status = executor_->Exec(stmt, &result_types);
status = executor_->FetchResult(&result_row);
ASSERT_FALSE(status.ok());
ASSERT_TRUE(EndsWith(status.msg().msg(), error_string)) << "Actual: '"
<< status.msg().msg() << "'" << endl << "Expected: '" << error_string << "'";
}
template <typename T> void TestFixedPointComparisons(bool test_boundaries) {
int64_t t_min = numeric_limits<T>::min();
int64_t t_max = numeric_limits<T>::max();
TestComparison(lexical_cast<string>(t_min), lexical_cast<string>(t_max), true);
TestBinaryPredicates(lexical_cast<string>(t_min), true);
TestBinaryPredicates(lexical_cast<string>(t_max), true);
if (test_boundaries) {
// this requires a cast of the second operand to a higher-resolution type
TestComparison(lexical_cast<string>(t_min - 1),
lexical_cast<string>(t_max), true);
// this requires a cast of the first operand to a higher-resolution type
TestComparison(lexical_cast<string>(t_min),
lexical_cast<string>(t_max + 1), true);
}
}
template <typename T> void TestFloatingPointComparisons(bool test_boundaries) {
// t_min is the smallest positive value
T t_min = numeric_limits<T>::min();
T t_max = numeric_limits<T>::max();
TestComparison(lexical_cast<string>(t_min), lexical_cast<string>(t_max), true);
TestComparison(lexical_cast<string>(-1.0 * t_max), lexical_cast<string>(t_max), true);
TestBinaryPredicates(lexical_cast<string>(t_min), true);
TestBinaryPredicates(lexical_cast<string>(t_max), true);
if (test_boundaries) {
// this requires a cast of the second operand to a higher-resolution type
TestComparison(lexical_cast<string>(numeric_limits<T>::min() - 1),
lexical_cast<string>(numeric_limits<T>::max()), true);
// this requires a cast of the first operand to a higher-resolution type
TestComparison(lexical_cast<string>(numeric_limits<T>::min()),
lexical_cast<string>(numeric_limits<T>::max() + 1), true);
}
// Compare nan: not equal to, larger than or smaller than anything, including itself
TestValue(lexical_cast<string>(t_min) + " < 0/0", TYPE_BOOLEAN, false);
TestValue(lexical_cast<string>(t_min) + " > 0/0", TYPE_BOOLEAN, false);
TestValue(lexical_cast<string>(t_min) + " = 0/0", TYPE_BOOLEAN, false);
TestValue(lexical_cast<string>(t_max) + " < 0/0", TYPE_BOOLEAN, false);
TestValue(lexical_cast<string>(t_max) + " > 0/0", TYPE_BOOLEAN, false);
TestValue(lexical_cast<string>(t_max) + " = 0/0", TYPE_BOOLEAN, false);
TestValue("0/0 < 0/0", TYPE_BOOLEAN, false);
TestValue("0/0 > 0/0", TYPE_BOOLEAN, false);
TestValue("0/0 = 0/0", TYPE_BOOLEAN, false);
// Compare inf: larger than everything except nan (or smaller, for -inf)
TestValue(lexical_cast<string>(t_max) + " < 1/0", TYPE_BOOLEAN, true);
TestValue(lexical_cast<string>(t_min) + " > -1/0", TYPE_BOOLEAN, true);
TestValue("1/0 = 1/0", TYPE_BOOLEAN, true);
TestValue("1/0 < 0/0", TYPE_BOOLEAN, false);
TestValue("0/0 < 1/0", TYPE_BOOLEAN, false);
}
void TestStringComparisons() {
TestValue<bool>("'abc' = 'abc'", TYPE_BOOLEAN, true);
TestValue<bool>("'abc' = 'abcd'", TYPE_BOOLEAN, false);
TestValue<bool>("'abc' != 'abcd'", TYPE_BOOLEAN, true);
TestValue<bool>("'abc' != 'abc'", TYPE_BOOLEAN, false);
TestValue<bool>("'abc' < 'abcd'", TYPE_BOOLEAN, true);
TestValue<bool>("'abcd' < 'abc'", TYPE_BOOLEAN, false);
TestValue<bool>("'abcd' < 'abcd'", TYPE_BOOLEAN, false);
TestValue<bool>("'abc' > 'abcd'", TYPE_BOOLEAN, false);
TestValue<bool>("'abcd' > 'abc'", TYPE_BOOLEAN, true);
TestValue<bool>("'abcd' > 'abcd'", TYPE_BOOLEAN, false);
TestValue<bool>("'abc' <= 'abcd'", TYPE_BOOLEAN, true);
TestValue<bool>("'abcd' <= 'abc'", TYPE_BOOLEAN, false);
TestValue<bool>("'abcd' <= 'abcd'", TYPE_BOOLEAN, true);
TestValue<bool>("'abc' >= 'abcd'", TYPE_BOOLEAN, false);
TestValue<bool>("'abcd' >= 'abc'", TYPE_BOOLEAN, true);
TestValue<bool>("'abcd' >= 'abcd'", TYPE_BOOLEAN, true);
// Test some empty strings
TestValue<bool>("'abcd' >= ''", TYPE_BOOLEAN, true);
TestValue<bool>("'' > ''", TYPE_BOOLEAN, false);
TestValue<bool>("'' = ''", TYPE_BOOLEAN, true);
}
void TestDecimalComparisons() {
TestValue("1.23 = 1.23", TYPE_BOOLEAN, true);
TestValue("1.23 = 1.230", TYPE_BOOLEAN, true);
TestValue("1.23 = 1.234", TYPE_BOOLEAN, false);
TestValue("1.23 != 1.234", TYPE_BOOLEAN, true);
TestValue("1.23 < 1.234", TYPE_BOOLEAN, true);
TestValue("1.23 > 1.234", TYPE_BOOLEAN, false);
TestValue("1.23 = 1.230000000000000000000", TYPE_BOOLEAN, true);
// Some values are too precise to be stored to full precision as doubles, but not too
// precise to be stored as decimals.
static const string not_too_precise = "1.25";
// The closest double to 'too_precise' is 1.25 - the string as written cannot be
// preresented exactly as a double.
static const string too_precise = "1.250000000000000000001";
TestValue(
"cast(" + not_too_precise + " as double) != cast(" + too_precise + " as double)",
TYPE_BOOLEAN, false);
TestValue(not_too_precise + " != " + too_precise, TYPE_BOOLEAN, true);
TestValue("cast(" + not_too_precise + " as double) IS DISTINCT FROM cast(" +
too_precise + " as double)",
TYPE_BOOLEAN, false);
TestValue(not_too_precise + " IS DISTINCT FROM " + too_precise, TYPE_BOOLEAN, true);
TestValue("cast(" + not_too_precise + " as double) IS NOT DISTINCT FROM cast(" +
too_precise + " as double)",
TYPE_BOOLEAN, true);
TestValue(
not_too_precise + " IS NOT DISTINCT FROM " + too_precise, TYPE_BOOLEAN, false);
TestValue(
"cast(" + not_too_precise + " as double) <=> cast(" + too_precise + " as double)",
TYPE_BOOLEAN, true);
TestValue(not_too_precise + " <=> " + too_precise, TYPE_BOOLEAN, false);
TestValue("cast(1 as decimal(38,0)) = cast(1 as decimal(38,37))", TYPE_BOOLEAN, true);
TestValue("cast(1 as decimal(38,0)) = cast(0.1 as decimal(38,38))",
TYPE_BOOLEAN, false);
TestValue("cast(1 as decimal(38,0)) > cast(0.1 as decimal(38,38))",
TYPE_BOOLEAN, true);
TestBinaryPredicates("cast(1 as decimal(8,0))", false);
TestBinaryPredicates("cast(1 as decimal(10,0))", false);
TestBinaryPredicates("cast(1 as decimal(38,0))", false);
}
// Test comparison operators with a left or right NULL operand against op.
void TestNullComparison(const string& op) {
// NULL as right operand.
TestIsNull(op + " = NULL", TYPE_BOOLEAN);
TestIsNull(op + " != NULL", TYPE_BOOLEAN);
TestIsNull(op + " <> NULL", TYPE_BOOLEAN);
TestIsNull(op + " < NULL", TYPE_BOOLEAN);
TestIsNull(op + " > NULL", TYPE_BOOLEAN);
TestIsNull(op + " <= NULL", TYPE_BOOLEAN);
TestIsNull(op + " >= NULL", TYPE_BOOLEAN);
// NULL as left operand.
TestIsNull("NULL = " + op, TYPE_BOOLEAN);
TestIsNull("NULL != " + op, TYPE_BOOLEAN);
TestIsNull("NULL <> " + op, TYPE_BOOLEAN);
TestIsNull("NULL < " + op, TYPE_BOOLEAN);
TestIsNull("NULL > " + op, TYPE_BOOLEAN);
TestIsNull("NULL <= " + op, TYPE_BOOLEAN);
TestIsNull("NULL >= " + op, TYPE_BOOLEAN);
}
void TestTimestampValue(const string& expr, const TimestampValue& expected_result);
void TestValidTimestampValue(const string& expr);
void TestDateValue(const string& expr, const DateValue& expected_result);
// Test IS DISTINCT FROM operator and its variants
void TestDistinctFrom() {
static const string operators[] = {"<=>", "IS DISTINCT FROM", "IS NOT DISTINCT FROM"};
static const string types[] = {"Boolean", "TinyInt", "SmallInt", "Int", "BigInt",
"Float", "Double", "String", "Timestamp", "Decimal"};
static const string operands1[] = {
"true", "cast(1 as TinyInt)", "cast(1 as SmallInt)", "cast(1 as Int)",
"cast(1 as BigInt)", "cast(1 as Float)", "cast(1 as Double)",
"'this is a string'", "cast(1 as TimeStamp)", "cast(1 as Decimal)"
};
static const string operands2[] = {
"false", "cast(2 as TinyInt)", "cast(2 as SmallInt)", "cast(2 as Int)",
"cast(2 as BigInt)", "cast(2 as Float)", "cast(2 as Double)",
"'this is ALSO a string'", "cast(2 as TimeStamp)", "cast(2 as Decimal)"
};
for (int i = 0; i < sizeof(operators) / sizeof(string); ++i) {
// "IS DISTINCT FROM" and "<=>" are generalized equality, and
// this fact is recorded in is_equal.
const bool is_equal = operators[i] != "IS DISTINCT FROM";
// Everything IS NOT DISTINCT FROM itself.
for (int j = 0; j < sizeof(types) / sizeof(string); ++j) {
const string operand = "cast(NULL as " + types[j] + ")";
TestValue(operand + ' ' + operators[i] + ' ' + operand, TYPE_BOOLEAN, is_equal);
}
for (int j = 0; j < sizeof(operands1) / sizeof(string); ++j) {
TestValue(operands1[j] + ' ' + operators[i] + ' ' + operands1[j], TYPE_BOOLEAN,
is_equal);
}
// NULL IS DISTINCT FROM all non-null things.
for (int j = 0; j < sizeof(operands1) / sizeof(string); ++j) {
TestValue("NULL " + operators[i] + ' ' + operands1[j], TYPE_BOOLEAN, !is_equal);
TestValue(operands1[j] + ' ' + operators[i] + " NULL", TYPE_BOOLEAN, !is_equal);
}
// Non-null values can be DISTINCT.
for (int j = 0; j < sizeof(operands1) / sizeof(string); ++j) {
TestValue(operands1[j] + ' ' + operators[i] + ' ' + operands2[j], TYPE_BOOLEAN,
!is_equal);
}
}
}
// Test comparison operators with a left or right NULL operand on all types.
void TestNullComparisons() {
unordered_map<int, string>::iterator def_iter;
for(def_iter = default_type_strs_.begin(); def_iter != default_type_strs_.end();
++def_iter) {
TestNullComparison(def_iter->second);
}
TestNullComparison("NULL");
}
// Generate all possible tests for combinations of <smaller> <op> <larger>.
// Also test conversions from strings.
void TestComparison(const string& smaller, const string& larger, bool compare_strings) {
// disabled for now, because our implicit casts from strings are broken
// and might return analysis errors when they shouldn't
// TODO: fix and re-enable tests
compare_strings = false;
string eq_pred = smaller + " = " + larger;
TestValue(eq_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
eq_pred = smaller + " = '" + larger + "'";
TestValue(eq_pred, TYPE_BOOLEAN, false);
}
string ne_pred = smaller + " != " + larger;
TestValue(ne_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
ne_pred = smaller + " != '" + larger + "'";
TestValue(ne_pred, TYPE_BOOLEAN, true);
}
string ne2_pred = smaller + " <> " + larger;
TestValue(ne2_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
ne2_pred = smaller + " <> '" + larger + "'";
TestValue(ne2_pred, TYPE_BOOLEAN, true);
}
string lt_pred = smaller + " < " + larger;
TestValue(lt_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
lt_pred = smaller + " < '" + larger + "'";
TestValue(lt_pred, TYPE_BOOLEAN, true);
}
string le_pred = smaller + " <= " + larger;
TestValue(le_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
le_pred = smaller + " <= '" + larger + "'";
TestValue(le_pred, TYPE_BOOLEAN, true);
}
string gt_pred = smaller + " > " + larger;
TestValue(gt_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
gt_pred = smaller + " > '" + larger + "'";
TestValue(gt_pred, TYPE_BOOLEAN, false);
}
string ge_pred = smaller + " >= " + larger;
TestValue(ge_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
ge_pred = smaller + " >= '" + larger + "'";
TestValue(ge_pred, TYPE_BOOLEAN, false);
}
}
// Generate all possible tests for combinations of <value> <op> <value>
// Also test conversions from strings.
void TestBinaryPredicates(const string& value, bool compare_strings) {
// disabled for now, because our implicit casts from strings are broken
// and might return analysis errors when they shouldn't
// TODO: fix and re-enable tests
compare_strings = false;
string eq_pred = value + " = " + value;
TestValue(eq_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
eq_pred = value + " = '" + value + "'";
TestValue(eq_pred, TYPE_BOOLEAN, true);
}
string ne_pred = value + " != " + value;
TestValue(ne_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
ne_pred = value + " != '" + value + "'";
TestValue(ne_pred, TYPE_BOOLEAN, false);
}
string ne2_pred = value + " <> " + value;
TestValue(ne2_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
ne2_pred = value + " <> '" + value + "'";
TestValue(ne2_pred, TYPE_BOOLEAN, false);
}
string lt_pred = value + " < " + value;
TestValue(lt_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
lt_pred = value + " < '" + value + "'";
TestValue(lt_pred, TYPE_BOOLEAN, false);
}
string le_pred = value + " <= " + value;
TestValue(le_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
le_pred = value + " <= '" + value + "'";
TestValue(le_pred, TYPE_BOOLEAN, true);
}
string gt_pred = value + " > " + value;
TestValue(gt_pred, TYPE_BOOLEAN, false);
if (compare_strings) {
gt_pred = value + " > '" + value + "'";
TestValue(gt_pred, TYPE_BOOLEAN, false);
}
string ge_pred = value + " >= " + value;
TestValue(ge_pred, TYPE_BOOLEAN, true);
if (compare_strings) {
ge_pred = value + " >= '" + value + "'";
TestValue(ge_pred, TYPE_BOOLEAN, true);
}
}
template <typename T> void TestFixedPointLimits(const ColumnType& type) {
// cast to non-char type first, otherwise we might end up interpreting
// the min/max as an ascii value
int64_t t_min = numeric_limits<T>::min();
int64_t t_max = numeric_limits<T>::max();
TestValue(lexical_cast<string>(t_min), type, numeric_limits<T>::min());
TestValue(lexical_cast<string>(t_max), type, numeric_limits<T>::max());
}
template <typename T> void TestFloatingPointLimits(const ColumnType& type) {
// numeric_limits<>::min() is the smallest positive value
TestValue(lexical_cast<string>(numeric_limits<T>::min()), type,
numeric_limits<T>::min());
TestValue(lexical_cast<string>(-1.0 * numeric_limits<T>::min()), type,
-1.0 * numeric_limits<T>::min());
TestValue(lexical_cast<string>(-1.0 * numeric_limits<T>::max()), type,
-1.0 * numeric_limits<T>::max());
TestValue(lexical_cast<string>(numeric_limits<T>::max() - 1.0), type,
numeric_limits<T>::max());
}
// Test ops that that always promote to a fixed type (e.g., next higher
// resolution type): ADD, SUBTRACT, MULTIPLY, DIVIDE.
// Note that adding the " " when generating the expression is not just cosmetic.
// We have "--" as a comment element in our lexer,
// so subtraction of a negative value will be ignored without " ".
template <typename LeftOp, typename RightOp, typename Result>
void TestFixedResultTypeOps(LeftOp a, RightOp b, const ColumnType& expected_type) {
Result cast_a = static_cast<Result>(a);
Result cast_b = static_cast<Result>(b);
string a_str = LiteralToString<Result>(cast_a);
string b_str = LiteralToString<Result>(cast_b);
TestValue(a_str + " + " + b_str, expected_type,
ArithmeticUtil::Compute<std::plus>(cast_a, cast_b));
TestValue(a_str + " - " + b_str, expected_type,
ArithmeticUtil::Compute<std::minus>(cast_a, cast_b));
TestValue(a_str + " * " + b_str, expected_type,
ArithmeticUtil::Compute<std::multiplies>(cast_a, cast_b));
TestValue(a_str + " / " + b_str, TYPE_DOUBLE,
static_cast<double>(a) / static_cast<double>(b));
}
// Test int ops that promote to assignment compatible type: BITAND, BITOR, BITXOR,
// BITNOT, INT_DIVIDE, MOD.
// As a convention we use RightOp as the higher resolution type.
template <typename LeftOp, typename RightOp>
void TestVariableResultTypeIntOps(LeftOp a, RightOp b,
const ColumnType& expected_type) {
RightOp cast_a = static_cast<RightOp>(a);
RightOp cast_b = static_cast<RightOp>(b);
string a_str = lexical_cast<string>(static_cast<int64_t>(a));
string b_str = lexical_cast<string>(static_cast<int64_t>(b));
TestValue(a_str + " & " + b_str, expected_type, cast_a & cast_b);
TestValue(a_str + " | " + b_str, expected_type, cast_a | cast_b);
TestValue(a_str + " ^ " + b_str, expected_type, cast_a ^ cast_b);
// Exclusively use b of type RightOp for unary op BITNOT.
TestValue("~" + b_str, expected_type, ~cast_b);
TestValue(a_str + " DIV " + b_str, expected_type, cast_a / cast_b);
TestValue(a_str + " % " + b_str, expected_type, cast_a % cast_b);
}
// Test ops that that always promote to a fixed type with NULL operands:
// ADD, SUBTRACT, MULTIPLY, DIVIDE.
// We need CastType to make lexical_cast work properly for low-resolution types.
template <typename NonNullOp, typename CastType>
void TestNullOperandFixedResultTypeOps(NonNullOp op, const ColumnType& expected_type) {
CastType cast_op = static_cast<CastType>(op);
string op_str = LiteralToString<CastType>(cast_op);
// NULL as right operand.
TestIsNull(op_str + " + NULL", expected_type);
TestIsNull(op_str + " - NULL", expected_type);
TestIsNull(op_str + " * NULL", expected_type);
TestIsNull(op_str + " / NULL", TYPE_DOUBLE);
// NULL as left operand.
TestIsNull("NULL + " + op_str, expected_type);
TestIsNull("NULL - " + op_str, expected_type);
TestIsNull("NULL * " + op_str, expected_type);
TestIsNull("NULL / " + op_str, TYPE_DOUBLE);
}
// Test binary int ops with NULL as left or right operand:
// BITAND, BITOR, BITXOR, INT_DIVIDE, MOD.
template <typename NonNullOp>
void TestNullOperandVariableResultTypeIntOps(NonNullOp op,
const ColumnType& expected_type) {
string op_str = lexical_cast<string>(static_cast<int64_t>(op));
// NULL as right operand.
TestIsNull(op_str + " & NULL", expected_type);
TestIsNull(op_str + " | NULL", expected_type);
TestIsNull(op_str + " ^ NULL", expected_type);
TestIsNull(op_str + " DIV NULL", expected_type);
TestIsNull(op_str + " % NULL", expected_type);
// NULL as left operand.
TestIsNull("NULL & " + op_str, expected_type);
TestIsNull("NULL | " + op_str, expected_type);
TestIsNull("NULL ^ " + op_str, expected_type);
TestIsNull("NULL DIV " + op_str, expected_type);
TestIsNull("NULL % " + op_str, expected_type);
}
// Test all binary ops with both operands being NULL.
// We expect such exprs to return TYPE_INT.
void TestNullOperandsArithmeticOps() {
TestIsNull("NULL + NULL", TYPE_DOUBLE);
TestIsNull("NULL - NULL", TYPE_DOUBLE);
TestIsNull("NULL * NULL", TYPE_DOUBLE);
TestIsNull("NULL / NULL", TYPE_DOUBLE);
TestIsNull("NULL & NULL", TYPE_INT);
TestIsNull("NULL | NULL", TYPE_INT);
TestIsNull("NULL ^ NULL", TYPE_INT);
TestIsNull("NULL DIV NULL", TYPE_INT);
TestIsNull("NULL % NULL", TYPE_DOUBLE);
TestIsNull("~NULL", TYPE_INT);
}
// Test factorial operator.
void TestFactorialArithmeticOp() {
// Basic exprs
TestValue("4!", TYPE_BIGINT, 24);
TestValue("0!", TYPE_BIGINT, 1);
TestValue("-20!", TYPE_BIGINT, 1);
TestIsNull("NULL!", TYPE_BIGINT);
TestValue("20!", TYPE_BIGINT, 2432902008176640000); // Largest valid value
TestError("21!"); // Overflow
// Compound exprs
TestValue("4 + (3!)", TYPE_BIGINT, 10);
// TestValue("5 + 3!", TYPE_BIGINT, 11); disabled b/c IMPALA-2149
TestValue("4! + 3", TYPE_BIGINT, 27);
TestValue("-1!", TYPE_BIGINT, 1); // Prefix takes precedence
// ! = should not be parsed as not equal operator
TestValue("4! = 25", TYPE_BOOLEAN, false);
// != should be parsed as a single token to avoid wacky behavior
TestValue("1 != 1", TYPE_BOOLEAN, false);
// Check factorial function exists as alias
TestValue("factorial(3!)", TYPE_BIGINT, 720);
}
template<typename T>
TimestampValue CreateTestTimestamp(T val);
// Parse the given string representation of a value into 'val' of type T.
// Returns false on parse failure.
template<class T>
bool ParseString(const string& str, T* val);
// Create a Literal expression out of 'str'. Adds the returned literal to pool_.
Literal* CreateLiteral(const ColumnType& type, const string& str);
// Helper function for LiteralConstruction test. Creates a Literal expression
// of 'type' from 'str' and verifies it compares equally to 'value'.
template <typename T>
void TestSingleLiteralConstruction(
const ColumnType& type, const T& value, const string& string_val);
// Test casting stmt to all types. Expected result is val.
template<typename T>
void TestCast(const string& stmt, T val, bool timestamp_out_of_range = false) {
TestValue("cast(" + stmt + " as boolean)", TYPE_BOOLEAN, static_cast<bool>(val));
TestValue("cast(" + stmt + " as tinyint)", TYPE_TINYINT, static_cast<int8_t>(val));
TestValue("cast(" + stmt + " as smallint)", TYPE_SMALLINT, static_cast<int16_t>(val));
TestValue("cast(" + stmt + " as int)", TYPE_INT, static_cast<int32_t>(val));
TestValue("cast(" + stmt + " as integer)", TYPE_INT, static_cast<int32_t>(val));
TestValue("cast(" + stmt + " as bigint)", TYPE_BIGINT, static_cast<int64_t>(val));
TestValue("cast(" + stmt + " as float)", TYPE_FLOAT, static_cast<float>(val));
TestValue("cast(" + stmt + " as double)", TYPE_DOUBLE, static_cast<double>(val));
TestValue("cast(" + stmt + " as real)", TYPE_DOUBLE, static_cast<double>(val));
TestStringValue("cast(" + stmt + " as string)", lexical_cast<string>(val));
if (!timestamp_out_of_range) {
TestTimestampValue("cast(" + stmt + " as timestamp)", CreateTestTimestamp(val));
} else {
TestIsNull("cast(" + stmt + " as timestamp)", TYPE_TIMESTAMP);
}
}
// Wrapper around UdfTestHarness::CreateTestContext() that stores the context in
// 'pool_' to be automatically cleaned up.
FunctionContext* CreateUdfTestContext(const FunctionContext::TypeDesc& return_type,
const std::vector<FunctionContext::TypeDesc>& arg_types,
RuntimeState* state = nullptr, MemPool* pool = nullptr) {
return pool_.Add(
UdfTestHarness::CreateTestContext(return_type, arg_types, state, pool));
}
};
template<>
TimestampValue ExprTest::CreateTestTimestamp(const string& val) {
return TimestampValue::ParseSimpleDateFormat(val);
}
template<>
TimestampValue ExprTest::CreateTestTimestamp(float val) {
return TimestampValue::FromSubsecondUnixTime(val, TimezoneDatabase::GetUtcTimezone());
}
template<>
TimestampValue ExprTest::CreateTestTimestamp(double val) {
return TimestampValue::FromSubsecondUnixTime(val, TimezoneDatabase::GetUtcTimezone());
}
template<>
TimestampValue ExprTest::CreateTestTimestamp(int val) {
return TimestampValue::FromUnixTime(val, TimezoneDatabase::GetUtcTimezone());
}
template<>
TimestampValue ExprTest::CreateTestTimestamp(int64_t val) {
return TimestampValue::FromUnixTime(val, TimezoneDatabase::GetUtcTimezone());
}
// Test casting 'stmt' to each of the native types. The result should be 'val'
// 'stmt' is a partial stmt that could be of any valid type.
template<>
void ExprTest::TestCast(const string& stmt, const char* val,
bool timestamp_out_of_range) {
try {
int8_t val8 = static_cast<int8_t>(lexical_cast<int16_t>(val));
#if 0
// Hive has weird semantics. What do we want to do?
TestValue(stmt + " as boolean)", TYPE_BOOLEAN, lexical_cast<bool>(val));
#endif
TestValue("cast(" + stmt + " as tinyint)", TYPE_TINYINT, val8);
TestValue("cast(" + stmt + " as smallint)", TYPE_SMALLINT, lexical_cast<int16_t>(val));
TestValue("cast(" + stmt + " as int)", TYPE_INT, lexical_cast<int32_t>(val));
TestValue("cast(" + stmt + " as bigint)", TYPE_BIGINT, lexical_cast<int64_t>(val));
TestValue("cast(" + stmt + " as float)", TYPE_FLOAT, lexical_cast<float>(val));
TestValue("cast(" + stmt + " as double)", TYPE_DOUBLE, lexical_cast<double>(val));
TestStringValue("cast(" + stmt + " as string)", lexical_cast<string>(val));
} catch (bad_lexical_cast& e) {
EXPECT_TRUE(false) << e.what();
}
}
template <>
bool ExprTest::ConvertValue<bool>(const string& value) {
if (value.compare("false") == 0) {
return false;
} else {
DCHECK(value.compare("true") == 0) << value;
return true;
}
}
template <>
int8_t ExprTest::ConvertValue<int8_t>(const string& value) {
StringParser::ParseResult result;
return StringParser::StringToInt<int8_t>(value.data(), value.size(), &result);
}
template <>
int16_t ExprTest::ConvertValue<int16_t>(const string& value) {
StringParser::ParseResult result;
return StringParser::StringToInt<int16_t>(value.data(), value.size(), &result);
}
template <>
int32_t ExprTest::ConvertValue<int32_t>(const string& value) {
StringParser::ParseResult result;
return StringParser::StringToInt<int32_t>(value.data(), value.size(), &result);
}
template <>
int64_t ExprTest::ConvertValue<int64_t>(const string& value) {
StringParser::ParseResult result;
return StringParser::StringToInt<int64_t>(value.data(), value.size(), &result);
}
template <>
double ExprTest::ConvertValue<double>(const string& value) {
StringParser::ParseResult result;
return StringParser::StringToFloat<double>(value.data(), value.size(), &result);
}
template <>
TimestampValue ExprTest::ConvertValue<TimestampValue>(const string& value) {
return TimestampValue::ParseSimpleDateFormat(value.data(), value.size());
}
template <>
DateValue ExprTest::ConvertValue<DateValue>(const string& value) {
return DateValue::ParseSimpleDateFormat(value.data(), value.size());
}
// We can't put this into TestValue() because GTest can't resolve
// the ambiguity in TimestampValue::operator==, even with the appropriate casts.
void ExprTest::TestTimestampValue(const string& expr, const TimestampValue& expected_result) {
EXPECT_EQ(expected_result,
ConvertValue<TimestampValue>(GetValue(expr, TYPE_TIMESTAMP)));
}
// Tests whether the returned TimestampValue is valid.
// We use this function for tests where the expected value is unknown, e.g., now().
void ExprTest::TestValidTimestampValue(const string& expr) {
EXPECT_TRUE(
ConvertValue<TimestampValue>(GetValue(expr, TYPE_TIMESTAMP)).HasDateOrTime());
}
// We can't put this into TestValue() because GTest can't resolve
// the ambiguity in DateValue::operator==, even with the appropriate casts.
void ExprTest::TestDateValue(const string& expr, const DateValue& expected_result) {
EXPECT_EQ(expected_result, ConvertValue<DateValue>(GetValue(expr, TYPE_DATE)));
}
template<class T>
bool ExprTest::ParseString(const string& str, T* val) {
istringstream stream(str);
stream >> *val;
return !stream.fail();
}
template<>
bool ExprTest::ParseString(const string& str, TimestampValue* val) {
boost::gregorian::date date;
boost::posix_time::time_duration time;
bool success = TimestampParser::ParseSimpleDateFormat(str.data(), str.length(), &date,
&time);
val->set_date(date);
val->set_time(time);
return success;
}
template<>
bool ExprTest::ParseString(const string& str, DateValue* val) {
return DateParser::ParseSimpleDateFormat(str.c_str(), str.length(), false, val);
}
Literal* ExprTest::CreateLiteral(const ColumnType& type, const string& str) {
switch (type.type) {
case TYPE_BOOLEAN: {
bool v = false;
EXPECT_TRUE(ParseString<bool>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_TINYINT: {
int8_t v = 0;
EXPECT_TRUE(ParseString<int8_t>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_SMALLINT: {
int16_t v = 0;
EXPECT_TRUE(ParseString<int16_t>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_INT: {
int32_t v = 0;
EXPECT_TRUE(ParseString<int32_t>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_BIGINT: {
int64_t v = 0;
EXPECT_TRUE(ParseString<int64_t>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_FLOAT: {
float v = 0;
EXPECT_TRUE(ParseString<float>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_DOUBLE: {
double v = 0;
EXPECT_TRUE(ParseString<double>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_STRING:
case TYPE_VARCHAR:
case TYPE_CHAR:
return pool_.Add(new Literal(type, str));
case TYPE_TIMESTAMP: {
TimestampValue v;
EXPECT_TRUE(ParseString<TimestampValue>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_DATE: {
DateValue v;
EXPECT_TRUE(ParseString<DateValue>(str, &v));
return pool_.Add(new Literal(type, v));
}
case TYPE_DECIMAL: {
double v = 0;
EXPECT_TRUE(ParseString<double>(str, &v));
return pool_.Add(new Literal(type, v));
}
default:
DCHECK(false) << "Invalid type: " << type.DebugString();
return nullptr;
}
}
template <typename T>
void ExprTest::TestSingleLiteralConstruction(
const ColumnType& type, const T& value, const string& string_val) {
ObjectPool pool;
RuntimeState state(TQueryCtx(), ExecEnv::GetInstance());
MemTracker tracker;
MemPool mem_pool(&tracker);
Literal* expr = CreateLiteral(type, string_val);
ScalarExprEvaluator* eval;
EXPECT_OK(
ScalarExprEvaluator::Create(*expr, &state, &pool, &mem_pool, &mem_pool, &eval));
EXPECT_OK(eval->Open(&state));
EXPECT_EQ(0, RawValue::Compare(eval->GetValue(nullptr), &value, type))
<< "type: " << type << ", value: " << value;
eval->Close(&state);
expr->Close();
state.ReleaseResources();
}
TEST_P(ExprTest, NullLiteral) {
for (int type = TYPE_BOOLEAN; type != TYPE_DATE; ++type) {
RuntimeState state(TQueryCtx(), ExecEnv::GetInstance());
ObjectPool pool;
MemTracker tracker;
MemPool mem_pool(&tracker);
NullLiteral expr(static_cast<PrimitiveType>(type));
ScalarExprEvaluator* eval;
EXPECT_OK(
ScalarExprEvaluator::Create(expr, &state, &pool, &mem_pool, &mem_pool, &eval));
EXPECT_OK(eval->Open(&state));
EXPECT_TRUE(eval->GetValue(nullptr) == nullptr);
eval->Close(&state);
state.ReleaseResources();
}
}
TEST_P(ExprTest, LiteralConstruction) {
bool b_val = true;
int8_t c_val = 'f';
int16_t s_val = 123;
int32_t i_val = 234;
int64_t l_val = 1234;
float f_val = 3.14f;
double d_val_1 = 1.23;
double d_val_2 = 7e6;
double d_val_3 = 5.9e-3;
string str_input = "Hello";
StringValue str_val(const_cast<char*>(str_input.data()), str_input.length());
TestSingleLiteralConstruction(TYPE_BOOLEAN, b_val, "1");
TestSingleLiteralConstruction(TYPE_TINYINT, c_val, "f");
TestSingleLiteralConstruction(TYPE_SMALLINT, s_val, "123");
TestSingleLiteralConstruction(TYPE_INT, i_val, "234");
TestSingleLiteralConstruction(TYPE_INT, i_val, "+234");
TestSingleLiteralConstruction(TYPE_BIGINT, l_val, "1234");
TestSingleLiteralConstruction(TYPE_BIGINT, l_val, "+1234");
TestSingleLiteralConstruction(TYPE_FLOAT, f_val, "3.14");
TestSingleLiteralConstruction(TYPE_FLOAT, f_val, "+3.14");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_1, "1.23");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_1, "+1.23");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_2, "7e6");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_2, "+7e6");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_3, "5.9e-3");
TestSingleLiteralConstruction(TYPE_DOUBLE, d_val_3, "+5.9e-3");
TestSingleLiteralConstruction(TYPE_STRING, str_val, "Hello");
TestSingleLiteralConstruction(TYPE_DATE, DateValue(2001, 11, 30), "2001-11-30");
// Min/Max Boundary value test for tiny/small/int/long/date
c_val = 127;
const char c_array_max[] = {(const char)127}; // avoid implicit casting
string c_input_max(c_array_max, 1);
s_val = 32767;
i_val = 2147483647;
l_val = 9223372036854775807l;
TestSingleLiteralConstruction(TYPE_TINYINT, c_val, c_input_max);
TestSingleLiteralConstruction(TYPE_SMALLINT, s_val, "32767");
TestSingleLiteralConstruction(TYPE_INT, i_val, "2147483647");
TestSingleLiteralConstruction(TYPE_BIGINT, l_val, "9223372036854775807");
TestSingleLiteralConstruction(TYPE_DATE, DateValue(9999, 12, 31), "9999-12-31");
const char c_array_min[] = {(const char)(-128)}; // avoid implicit casting
string c_input_min(c_array_min, 1);
c_val = -128;
s_val = -32768;
i_val = -2147483648;
l_val = -9223372036854775807l-1;
TestSingleLiteralConstruction(TYPE_TINYINT, c_val, c_input_min);
TestSingleLiteralConstruction(TYPE_SMALLINT, s_val, "-32768");
TestSingleLiteralConstruction(TYPE_INT, i_val, "-2147483648");
TestSingleLiteralConstruction(TYPE_BIGINT, l_val, "-9223372036854775808");
TestSingleLiteralConstruction(TYPE_DATE, DateValue(1, 1, 1), "0001-01-01");
}
TEST_P(ExprTest, LiteralExprs) {
TestFixedPointLimits<int8_t>(TYPE_TINYINT);
TestFixedPointLimits<int16_t>(TYPE_SMALLINT);
TestFixedPointLimits<int32_t>(TYPE_INT);
TestFixedPointLimits<int64_t>(TYPE_BIGINT);
// The value is not an exact FLOAT so it gets compared as a DOUBLE
// and fails. This needs to be researched.
// TestFloatingPointLimits<float>(TYPE_FLOAT);
TestFloatingPointLimits<double>(TYPE_DOUBLE);
TestValue("true", TYPE_BOOLEAN, true);
TestValue("false", TYPE_BOOLEAN, false);
TestStringValue("'test'", "test");
TestIsNull("null", TYPE_NULL);
}
// IMPALA-3942: Test escaping string literal for single/double quotes
TEST_P(ExprTest, EscapeStringLiteral) {
TestStringValue(R"('"')", R"(")");
TestStringValue(R"("'")", R"(')");
TestStringValue(R"("\"")", R"(")");
TestStringValue(R"('\'')", R"(')");
TestStringValue(R"('\\"')", R"(\")");
TestStringValue(R"("\\'")", R"(\')");
TestStringValue(R"("\\\"")", R"(\")");
TestStringValue(R"('\\\'')", R"(\')");
TestStringValue(R"("\\\"\\'\\\"")", R"(\"\'\")");
TestStringValue(R"('\\\'\\"\\\'')", R"(\'\"\')");
TestStringValue(R"("\\")", R"(\)");
TestStringValue(R"('\\')", R"(\)");
TestStringValue(R"("\\\\")", R"(\\)");
TestStringValue(R"('\\\\')", R"(\\)");
TestStringValue(R"('a"b')", R"(a"b)");
TestStringValue(R"("a'b")", R"(a'b)");
TestStringValue(R"('a\"b')", R"(a"b)");
TestStringValue(R"('a\'b')", R"(a'b)");
TestStringValue(R"("a\"b")", R"(a"b)");
TestStringValue(R"("a\'b")", R"(a'b)");
TestStringValue(R"('a\\"b')", R"(a\"b)");
TestStringValue(R"("a\\'b")", R"(a\'b)");
TestStringValue(R"('a\\\'b')", R"(a\'b)");
TestStringValue(R"("a\\\"b")", R"(a\"b)");
TestStringValue(R"(concat("a'b", "c'd"))", R"(a'bc'd)");
TestStringValue(R"(concat('a"b', 'c"d'))", R"(a"bc"d)");
TestStringValue(R"(concat("a'b", 'c"d'))", R"(a'bc"d)");
TestStringValue(R"(concat('a"b', "c'd"))", R"(a"bc'd)");
TestStringValue(R"(concat("a\"b", 'c\'d'))", R"(a"bc'd)");
TestStringValue(R"(concat('a\'b', "c\"d"))", R"(a'bc"d)");
TestStringValue(R"(concat("a\\\"b", 'c\\\'d'))", R"(a\"bc\'d)");
TestStringValue(R"(concat('a\\\'b', "c\\\"d"))", R"(a\'bc\"d)");
}
TEST_P(ExprTest, ArithmeticExprs) {
// Test float ops.
TestFixedResultTypeOps<float, float, double>(min_float_values_[TYPE_FLOAT],
min_float_values_[TYPE_FLOAT], TYPE_DOUBLE);
TestFixedResultTypeOps<float, double, double>(min_float_values_[TYPE_FLOAT],
min_float_values_[TYPE_DOUBLE], TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(min_float_values_[TYPE_DOUBLE],
min_float_values_[TYPE_DOUBLE], TYPE_DOUBLE);
// Test behavior of float ops at max/min value boundaries.
// The tests with float type should trivially pass, since their results are double.
TestFixedResultTypeOps<float, float, double>(numeric_limits<float>::min(),
numeric_limits<float>::min(), TYPE_DOUBLE);
TestFixedResultTypeOps<float, float, double>(numeric_limits<float>::max(),
numeric_limits<float>::max(), TYPE_DOUBLE);
TestFixedResultTypeOps<float, float, double>(numeric_limits<float>::min(),
numeric_limits<float>::max(), TYPE_DOUBLE);
TestFixedResultTypeOps<float, float, double>(numeric_limits<float>::max(),
numeric_limits<float>::min(), TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(numeric_limits<double>::min(),
numeric_limits<double>::min(), TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(numeric_limits<double>::max(),
numeric_limits<double>::max(), TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(numeric_limits<double>::min(),
numeric_limits<double>::max(), TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(numeric_limits<double>::max(),
numeric_limits<double>::min(), TYPE_DOUBLE);
// Test behavior with zero (especially for division by zero).
TestFixedResultTypeOps<float, float, double>(min_float_values_[TYPE_FLOAT],
0.0f, TYPE_DOUBLE);
TestFixedResultTypeOps<double, double, double>(min_float_values_[TYPE_DOUBLE],
0.0, TYPE_DOUBLE);
// Test ops that always promote to fixed type (e.g., next higher resolution type).
TestFixedResultTypeOps<int8_t, int8_t, int16_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_TINYINT], TYPE_SMALLINT);
TestFixedResultTypeOps<int8_t, int16_t, int32_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_SMALLINT], TYPE_INT);
TestFixedResultTypeOps<int8_t, int32_t, int64_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_INT], TYPE_BIGINT);
TestFixedResultTypeOps<int8_t, int64_t, int64_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestFixedResultTypeOps<int16_t, int16_t, int32_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_SMALLINT], TYPE_INT);
TestFixedResultTypeOps<int16_t, int32_t, int64_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_INT], TYPE_BIGINT);
TestFixedResultTypeOps<int16_t, int64_t, int64_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestFixedResultTypeOps<int32_t, int32_t, int64_t>(min_int_values_[TYPE_INT],
min_int_values_[TYPE_INT], TYPE_BIGINT);
TestFixedResultTypeOps<int32_t, int64_t, int64_t>(min_int_values_[TYPE_INT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestFixedResultTypeOps<int64_t, int64_t, int64_t>(min_int_values_[TYPE_BIGINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
// Test behavior on overflow/underflow.
TestFixedResultTypeOps<int64_t, int64_t, int64_t>(numeric_limits<int64_t>::min()+1,
numeric_limits<int64_t>::min()+1, TYPE_BIGINT);
TestFixedResultTypeOps<int64_t, int64_t, int64_t>(numeric_limits<int64_t>::max(),
numeric_limits<int64_t>::max(), TYPE_BIGINT);
TestFixedResultTypeOps<int64_t, int64_t, int64_t>(numeric_limits<int64_t>::min()+1,
numeric_limits<int64_t>::max(), TYPE_BIGINT);
TestFixedResultTypeOps<int64_t, int64_t, int64_t>(numeric_limits<int64_t>::max(),
numeric_limits<int64_t>::min()+1, TYPE_BIGINT);
// Test behavior with NULLs.
TestNullOperandFixedResultTypeOps<float, double>(min_float_values_[TYPE_FLOAT],
TYPE_DOUBLE);
TestNullOperandFixedResultTypeOps<double, double>(min_float_values_[TYPE_DOUBLE],
TYPE_DOUBLE);
TestNullOperandFixedResultTypeOps<int8_t, int64_t>(min_int_values_[TYPE_TINYINT],
TYPE_SMALLINT);
TestNullOperandFixedResultTypeOps<int16_t, int64_t>(min_int_values_[TYPE_SMALLINT],
TYPE_INT);
TestNullOperandFixedResultTypeOps<int32_t, int64_t>(min_int_values_[TYPE_INT],
TYPE_BIGINT);
TestNullOperandFixedResultTypeOps<int64_t, int64_t>(min_int_values_[TYPE_BIGINT],
TYPE_BIGINT);
// Test int ops that promote to assignment compatible type.
TestVariableResultTypeIntOps<int8_t, int8_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_TINYINT], TYPE_TINYINT);
TestVariableResultTypeIntOps<int8_t, int16_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_SMALLINT], TYPE_SMALLINT);
TestVariableResultTypeIntOps<int8_t, int32_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_INT], TYPE_INT);
TestVariableResultTypeIntOps<int8_t, int64_t>(min_int_values_[TYPE_TINYINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestVariableResultTypeIntOps<int16_t, int16_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_SMALLINT], TYPE_SMALLINT);
TestVariableResultTypeIntOps<int16_t, int32_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_INT],TYPE_INT);
TestVariableResultTypeIntOps<int16_t, int64_t>(min_int_values_[TYPE_SMALLINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestVariableResultTypeIntOps<int32_t, int32_t>(min_int_values_[TYPE_INT],
min_int_values_[TYPE_INT], TYPE_INT);
TestVariableResultTypeIntOps<int32_t, int64_t>(min_int_values_[TYPE_INT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
TestVariableResultTypeIntOps<int64_t, int64_t>(min_int_values_[TYPE_BIGINT],
min_int_values_[TYPE_BIGINT], TYPE_BIGINT);
// Test behavior of INT_DIVIDE and MOD with zero as second argument.
IntValMap::iterator int_iter;
for(int_iter = min_int_values_.begin(); int_iter != min_int_values_.end();
++int_iter) {
string& val = default_type_strs_[int_iter->first];
TestIsNull(val + " DIV 0", static_cast<PrimitiveType>(int_iter->first));
TestIsNull(val + " % 0", static_cast<PrimitiveType>(int_iter->first));
}
// Test behavior with NULLs.
TestNullOperandVariableResultTypeIntOps<int8_t>(min_int_values_[TYPE_TINYINT],
TYPE_TINYINT);
TestNullOperandVariableResultTypeIntOps<int16_t>(min_int_values_[TYPE_SMALLINT],
TYPE_SMALLINT);
TestNullOperandVariableResultTypeIntOps<int32_t>(min_int_values_[TYPE_INT],
TYPE_INT);
TestNullOperandVariableResultTypeIntOps<int64_t>(min_int_values_[TYPE_BIGINT],
TYPE_BIGINT);
// Tests for dealing with '-'.
TestValue("-1", TYPE_TINYINT, -1);
TestValue("1 - 1", TYPE_SMALLINT, 0);
TestValue("1 - - 1", TYPE_SMALLINT, 2);
TestValue("1 - - - 1", TYPE_SMALLINT, 0);
TestValue("- 1 - 1", TYPE_SMALLINT, -2);
TestValue("- 1 - - 1", TYPE_SMALLINT, 0);
// The "--" indicates a comment to be ignored.
// Therefore, the result should be -1.
TestValue("- 1 --1", TYPE_TINYINT, -1);
// Test all arithmetic exprs with only NULL operands.
TestNullOperandsArithmeticOps();
// Test behavior of factorial operator.
TestFactorialArithmeticOp();
}
// Use a table-driven approach to test DECIMAL expressions to make it easier to test
// both the old (decimal_v2=false) and new (decimal_v2=true) DECIMAL semantics.
struct DecimalExpectedResult {
bool error;
bool null;
int128_t scaled_val;
int precision;
int scale;
};
struct DecimalTestCase {
// Return the expected result for the given DECIMAL version. When the expected V1 and
// V2 results are the same, allows the V2 expected result to be unspecified in the
// test case table. When V2 expected results aren't specified, V2 entries have both
// 'null' set to false and 'precision' set to 0, which is an illegal combination and
// signals that V1 results should be used instead.
const DecimalExpectedResult& Expected(bool v2) const {
if (v2 && (expected[1].null || expected[1].precision != 0)) return expected[1];
// Either testing version 1, or the results for version 2 were not specified.
return expected[0];
}
// Expression to execute.
string expr;
// Expected results for version 1 and Version 2. Version 2 results can be left unset
// when the expected result is the same between versions.
DecimalExpectedResult expected[2];
};
// Utility function to construct int128 types.
int128_t StringToInt128(string s) {
int128_t result = 0;
int sign = 1;
int digit = 0;
for (int i = 0; i < s.length(); ++i) {
switch (s[i]) {
case '-':
digit = -1;
break;
case '0':
digit = 0;
break;
case '1':
digit = 1;
break;
case '2':
digit = 2;
break;
case '3':
digit = 3;
break;
case '4':
digit = 4;
break;
case '5':
digit = 5;
break;
case '6':
digit = 6;
break;
case '7':
digit = 7;
break;
case '8':
digit = 8;
break;
case '9':
digit = 9;
break;
default:
DCHECK(false) << "Unexpected character.";
}
if (digit == -1) {
DCHECK_EQ(sign, 1) << "Minus symbol appears multiple times.";
sign = -1;
} else {
result = result * 10 + digit;
}
}
return sign * result;
}
// Format is:
// { Test Expression (as a string),
// { expected error, expected null, scaled_val, precision, scale for V1
// expected error, expected null, scaled_val, precision, scale for V2 }}
DecimalTestCase decimal_cases[] = {
// Test add/subtract operators
{ "1.23 + cast(1 as decimal(4,3))", {{ false, false, 2230, 5, 3 }}},
{ "1.23 - cast(0.23 as decimal(10,3))", {{ false, false, 1000, 11, 3 }}},
{ "cast(1.23 as decimal(8,2)) + cast(1 as decimal(20,3))",
{{ false, false, 2230, 21, 3 }}},
{ "cast(1.23 as decimal(30,2)) - cast(1 as decimal(4,3))",
{{ false, false, 230, 32, 3 }}},
{ "cast(1 as decimal(38,0)) + cast(0.2 as decimal(38,1))",
{{ false, false, 12, 38, 1 }}},
{ "cast(88928 as decimal(11,2)) + cast(0 as decimal(9,1))",
{{ false, false, 8892800, 12, 2 }}},
{ "cast(100000000000000000000000000000000 as decimal(38,0)) + "
"cast(0 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ true, false, 0, 38, 6 }}},
{ "cast(-100000000000000000000000000000000 as decimal(38,0)) + "
"cast(0 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ true, false, 0, 38, 6 }}},
{ "cast(99999999999999999999999999999999 as decimal(38,0)) + "
"cast(0 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("99999999999999999999999999999999000000"), 38, 6 }}},
{ "cast(-99999999999999999999999999999999 as decimal(38,0)) + "
"cast(0 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("-99999999999999999999999999999999000000"), 38, 6 }}},
{ "cast(99999999999999999999999999.9999999999 as decimal(36,10)) + "
"cast(99999999999999999999999999.9999999999 as decimal(36,10))",
{{ false, false, StringToInt128("1999999999999999999999999999999999998"), 37, 10 }}},
{ "cast(99999999999999999999999999.9999999999 as decimal(36,10)) + "
"cast(-99999999999999999999999999.9999999999 as decimal(36,10))",
{{ false, false, 0, 37, 10 }}},
{ "cast(999999999999999999999999999.9999999999 as decimal(37,10)) + "
"cast(999999999999999999999999999.9999999999 as decimal(37,10))",
{{ false, false, StringToInt128("19999999999999999999999999999999999998"), 38, 10 }}},
{ "cast(999999999999999999999999999.9999999999 as decimal(37,10)) + "
"cast(-999999999999999999999999999.9999999999 as decimal(37,10))",
{{ false, false, 0, 38, 10 }}},
// Largest simple case where we don't call AddLarge() or AubtractLarge().
// We are adding (2^125 - 1) + (2^125 - 1) here.
{ "cast(42535295865117307932921825928971026431 as decimal(38,0)) + "
"cast(42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, StringToInt128("85070591730234615865843651857942052862"), 38, 0 }}},
{ "cast(-42535295865117307932921825928971026431 as decimal(38,0)) + "
"cast(-42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, StringToInt128("-85070591730234615865843651857942052862"), 38, 0 }}},
{ "cast(42535295865117307932921825928971026431 as decimal(38,0)) + "
"cast(-42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, 0, 38, 0 }}},
// Smallest case where we call AddLarge(). We are adding (2^125) + (2^125 - 1) here.
{ "cast(42535295865117307932921825928971026432 as decimal(38,0)) + "
"cast(42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, StringToInt128("85070591730234615865843651857942052863"), 38, 0 }}},
{ "cast(-42535295865117307932921825928971026432 as decimal(38,0)) + "
"cast(-42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, StringToInt128("-85070591730234615865843651857942052863"), 38, 0 }}},
{ "cast(42535295865117307932921825928971026432 as decimal(38,0)) + "
"cast(-42535295865117307932921825928971026431 as decimal(38,0))",
{{ false, false, 1, 38, 0 }}},
{ "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(8899999999999999999999999999999.999999 as decimal(38,6))",
{{ false, true, 0, 38, 6 },
{ true, false, 0, 38, 6 }}},
{ "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(-8899999999999999999999999999999.999999 as decimal(38,6))",
{{ false, true, 0, 38, 6 },
{ true, false, 0, 38, 6 }}},
{ "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(8899999999999999999999999999999.999999 as decimal(38,6))",
{{ false, false, StringToInt128("-91100000000000000000000000000000000000"), 38, 6 }}},
// Close to the maximum value.
{ "cast(77777777777777777777777777777777.777777 as decimal(38,6)) + "
"cast(22222222222222222222222222222222.222222 as decimal(38,6))",
{{ false, false, StringToInt128("99999999999999999999999999999999999999"), 38, 6 }}},
{ "cast(-77777777777777777777777777777777.777777 as decimal(38,6)) + "
"cast(22222222222222222222222222222222.222222 as decimal(38,6))",
{{ false, false, StringToInt128("-55555555555555555555555555555555555555"), 38, 6 }}},
{ "cast(-77777777777777777777777777777777.777777 as decimal(38,6)) + "
"cast(-22222222222222222222222222222222.222222 as decimal(38,6))",
{{ false, false, StringToInt128("-99999999999999999999999999999999999999"), 38, 6 }}},
// Smallest result that overflows.
{ "cast(77777777777777777777777777777777.777777 as decimal(38,6)) + "
"cast(22222222222222222222222222222222.222223 as decimal(38,6))",
{{ false, true, 0, 38, 6 },
{ true, false, 0, 38, 6 }}},
{ "cast(-77777777777777777777777777777777.777777 as decimal(38,6)) + "
"cast(-22222222222222222222222222222222.222223 as decimal(38,6))",
{{ false, true, 0, 38, 6 },
{ true, false, 0, 38, 6 }}},
{ "cast(11111111111111111111111111111111.777777 as decimal(38,6)) + "
"cast(11111111111111111111111111111111.555555 as decimal(38,6))",
{{ false, false, StringToInt128("22222222222222222222222222222223333332"), 38, 6 }}},
{ "cast(-11111111111111111111111111111111.777777 as decimal(38,6)) + "
"cast(11111111111111111111111111111111.555555 as decimal(38,6))",
{{ false, false, -222222, 38, 6 }}},
{ "cast(11111111111111111111111111111111.777777 as decimal(38,6)) + "
"cast(-11111111111111111111111111111111.555555 as decimal(38,6))",
{{ false, false, 222222, 38, 6 }}},
{ "cast(-11111111111111111111111111111111.777777 as decimal(38,6)) + "
"cast(-11111111111111111111111111111111.555555 as decimal(38,6))",
{{ false, false, StringToInt128("-22222222222222222222222222222223333332"), 38, 6 }}},
{ "cast(3333333333333333333333333333333.8888884 as decimal(38,7)) + "
"cast(3333333333333333333333333333333.1111111 as decimal(38,7))",
{{ false, false, StringToInt128("66666666666666666666666666666669999995"), 38, 7 },
{ false, false, StringToInt128("6666666666666666666666666666667000000"), 38, 6 }}},
{ "cast(-3333333333333333333333333333333.8888884 as decimal(38,7)) + "
"cast(-3333333333333333333333333333333.1111111 as decimal(38,7))",
{{ false, false, StringToInt128("-66666666666666666666666666666669999995"), 38, 7 },
{ false, false, StringToInt128("-6666666666666666666666666666667000000"), 38, 6 }}},
{ "cast(3333333333333333333333333333333.9999995 as decimal(38,7)) + "
"cast(0 as decimal(38,7))",
{{ false, false, StringToInt128("33333333333333333333333333333339999995"), 38, 7 },
{ false, false, StringToInt128("3333333333333333333333333333334000000"), 38, 6 }}},
{ "cast(-3333333333333333333333333333333.9999995 as decimal(38,7)) + "
"cast(0 as decimal(38,7))",
{{ false, false, StringToInt128("-33333333333333333333333333333339999995"), 38, 7 },
{ false, false, StringToInt128("-3333333333333333333333333333334000000"), 38, 6 }}},
// overflow due to rounding
{ "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(0.0000005 as decimal(38,7))",
{{ false, true, 0, 38, 7 },
{ true, false, 0, 38, 6 }}},
{ "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(-0.0000005 as decimal(38,7))",
{{ false, true, 0, 38, 7 },
{ true, false, 0, 38, 6 }}},
{ "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(-0.0000006 as decimal(38,7))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("99999999999999999999999999999999999998"), 38, 6 }}},
{ "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(0.0000004 as decimal(38,7))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("99999999999999999999999999999999999999"), 38, 6 }}},
{ "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
"cast(-0.0000004 as decimal(38,7))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("-99999999999999999999999999999999999999"), 38, 6 }}},
{ "cast(99999999999999999999999999999999 as decimal(38,0)) + "
"cast(0.9999994 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("99999999999999999999999999999999999999"), 38, 6 }}},
{ "cast(-99999999999999999999999999999999 as decimal(38,0)) + "
"cast(-0.9999994 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("-99999999999999999999999999999999999999"), 38, 6 }}},
{ "cast(99999999999999999999999999999999 as decimal(38,0)) + "
"cast(0.9999995 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ true, false, 0, 38, 6 }}},
{ "cast(-99999999999999999999999999999999 as decimal(38,0)) + "
"cast(-0.9999995 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ true, false, 0, 38, 6 }}},
{ "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) + "
"cast(-5e-38 as decimal(38,38))",
{{ false, false, StringToInt128("99999999999999999999999999999999999994"), 38, 38 },
{ false, false, StringToInt128("9999999999999999999999999999999999999"), 38, 37 }}},
{ "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) + "
"cast(-4e-38 as decimal(38,38))",
{{ false, false, StringToInt128("99999999999999999999999999999999999995"), 38, 38 },
{ false, false, StringToInt128("10000000000000000000000000000000000000"), 38, 37 }}},
{ "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) + "
"cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("20000000000000000000000000000000000000"), 38, 37 }}},
{ "cast(-0.99999999999999999999999999999999999999 as decimal(38,38)) + "
"cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(0 as decimal(38,38)) + cast(0 as decimal(38,38))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 37 }}},
// IMPALA-6292
{ "cast(1 as decimal(13,12)) - "
"cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
{{ false, false, 1, 38, 38 },
{ false, false, 0, 38, 36 }}},
{ "cast(0.1 as decimal(13,12)) - "
"cast(99999999999999999999999999999999999999 as decimal(38,0))",
{{ false, true, 0, 38, 12 },
{ true, false, 0, 38, 6 }}},
// Test multiply operator
{ "cast(1.23 as decimal(30,2)) * cast(1 as decimal(10,3))",
{{ false, false, 123000, 38, 5 }}},
{ "cast(1.23 as decimal(3,2)) * cast(1 as decimal(20,3))",
{{ false, false, 123000, 23, 5 },
{ false, false, 123000, 24, 5 }}},
{ "cast(0.1 as decimal(20,20)) * cast(1 as decimal(20,19))",
{{ false, false, StringToInt128("10000000000000000000000000000000000000"), 38, 38 },
{ false, false, StringToInt128("100000000000000000000000000000000000"), 38, 36 }}},
{ "cast(111.22 as decimal(5,2)) * cast(3333.444 as decimal(7,3))",
{{ false, false, 37074564168, 12, 5 },
{ false, false, 37074564168, 13, 5 }}},
{ "cast(0.01 as decimal(38,38)) * cast(25 as decimal(38,0))",
{{ false, false, StringToInt128("25000000000000000000000000000000000000"), 38, 38 },
{ false, false, 250000, 38, 6 }}},
{ "cast(-0.01 as decimal(38,38)) * cast(25 as decimal(38,0))",
{{ false, false, StringToInt128("-25000000000000000000000000000000000000"), 38, 38 },
{ false, false, -250000, 38, 6 }}},
{ "cast(-0.01 as decimal(38,38)) * cast(-25 as decimal(38,0))",
{{ false, false, StringToInt128("25000000000000000000000000000000000000"), 38, 38 },
{ false, false, 250000, 38, 6 }}},
{ "cast(0.1 as decimal(38,38)) * cast(25 as decimal(38,0))",
{{ false, true, 0, 38, 38 },
{ false, false, 2500000, 38, 6 }}},
{ "cast(-0.1 as decimal(38,38)) * cast(25 as decimal(38,0))",
{{ false, true, 0, 38, 38 },
{ false, false, -2500000, 38, 6 }}},
{ "cast(-0.1 as decimal(38,38)) * cast(-25 as decimal(38,0))",
{{ false, true, 0, 38, 38 },
{ false, false, 2500000, 38, 6 }}},
{ "cast(9999999999999999.9999 as decimal(20,4)) * "
"cast(9999999999999999.994 as decimal(19,3))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("99999999999999999939000000000000000001"), 38, 6 }}},
{ "cast(9.99999 as decimal(6,5)) * "
"cast(9999999999999999999999999999999.94 as decimal(33,2))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("99999899999999999999999999999999400001"), 38, 6 }}},
{ "cast(0 as decimal(38,38)) * cast(1 as decimal(5,2))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 34 }}},
{ "cast(12345.67 as decimal(7,2)) * cast(12345.67 as decimal(7,2))",
{{ false, false, 1524155677489, 14, 4 },
{ false, false, 1524155677489, 15, 4 }}},
{ "cast(2643918831543678.5772617359442611897419 as decimal(38,22)) * "
"cast(3972211379387512.6946776728996748717839 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("10502204468834736291038133384309593605"), 38, 6 }}},
{ "cast(-2643918831543678.5772617359442611897419 as decimal(38,22)) * "
"cast(3972211379387512.6946776728996748717839 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("-10502204468834736291038133384309593605"), 38, 6 }}},
{ "cast(-2643918831543678.5772617359442611897419 as decimal(38,22)) * "
"cast(-3972211379387512.6946776728996748717839 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("10502204468834736291038133384309593605"), 38, 6 }}},
{ "cast(2545664579818579.6268123468146829994472 as decimal(38,22)) * "
"cast(8862165565622381.6689679519457799681439 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("22560100980892785285767018366785939200"), 38, 6 }}},
{ "cast(2575543652687181.7412422395638291836214 as decimal(38,22)) * "
"cast(9142529549684737.3986798331295277312253 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("23546983951195523383767823614338114083"), 38, 6 }}},
{ "cast(6188696551164477.9944646378584981371524 as decimal(38,22)) * "
"cast(9234914917975734.1781526147879384775182 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("57152086103173814294786121078946977968"), 38, 6 }}},
{ "cast(-6188696551164477.9944646378584981371524 as decimal(38,22)) * "
"cast(9234914917975734.1781526147879384775182 as decimal(38,22))",
{{ false, true, 0, 38, 38 },
{ false, false, StringToInt128("-57152086103173814294786121078946977968"), 38, 6 }}},
{ "cast(1.006 as decimal(4,2)) * cast(1.1 as decimal(4,2))",
{{ false, false, 11000, 8, 4 },
{ false, false, 11110, 9, 4 }}},
{ "cast(0.9994 as decimal(38,4)) * cast(0.999 as decimal(38,3))",
{{ false, false, 9984006, 38, 7 },
{ false, false, 998401, 38, 6 }}},
{ "cast(0.000000000000000000000000000000000001 as decimal(38,38)) * "
"cast(0.000000000000000000000000000000000001 as decimal(38,38))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(0.00000000000000000000000000000000001 as decimal(38,37)) * "
"cast(0.00000000000000000000000000000000001 as decimal(38,37))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 35 }}},
{ "cast(0.00000000000000001 as decimal(38,37)) * "
"cast(0.000000000000000001 as decimal(38,37))",
{{ false, false, 1000, 38, 38 },
{ false, false, 1, 38, 35 }}},
{ "cast(9e-18 as decimal(38,38)) * cast(1e-21 as decimal(38,38))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(1e-37 as decimal(38,38)) * cast(0.1 as decimal(38,38))",
{{ false, false, 1, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(1e-37 as decimal(38,38)) * cast(-0.1 as decimal(38,38))",
{{ false, false, -1, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(9e-36 as decimal(38,38)) * cast(0.1 as decimal(38,38))",
{{ false, false, 90, 38, 38 },
{ false, false, 9, 38, 37 }}},
{ "cast(9e-37 as decimal(38,38)) * cast(0.1 as decimal(38,38))",
{{ false, false, 9, 38, 38 },
{ false, false, 1, 38, 37 }}},
// We are multiplying (2^64 - 1) * (2^63 - 1), which produces the largest intermediate
// result that does not require int256. It overflows because the result is larger than
// MAX_UNSCALED_DECIMAL16.
{ "cast(18446744073709551615 as decimal(38,0)) * "
"cast(9223372036854775807 as decimal(38,0))",
{{ false, true, 0, 38, 0 },
{ true, false, 0, 38, 0 }}},
{ "cast(-18446744073709551615 as decimal(38,0)) * "
"cast(9223372036854775807 as decimal(38,0))",
{{ false, true, 0, 38, 0 },
{ true, false, 0, 38, 0 }}},
// int256 is required. We are multiplying (2^64 - 1) * (2^63) here.
{ "cast(18446744073709551615 as decimal(38,0)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, true, 0, 38, 0 },
{ true, false, 0, 38, 0 }}},
{ "cast(-18446744073709551615 as decimal(38,0)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, true, 0, 38, 0 },
{ true, false, 0, 38, 0 }}},
// Largest intermediate result that does not require int256.
{ "cast(1844674407370.9551615 as decimal(38,7)) * "
"cast(9223372036854775807 as decimal(38,0))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("17014118346046923170401718760531977831"), 38, 6 }}},
{ "cast(-1844674407370.9551615 as decimal(38,7)) * "
"cast(9223372036854775807 as decimal(38,0))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("-17014118346046923170401718760531977831"), 38, 6 }}},
// int256 is required.
{ "cast(1844674407370.9551615 as decimal(38,7)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("17014118346046923172246393167902932992"), 38, 6 }}},
{ "cast(-1844674407370.9551615 as decimal(38,7)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, true, 0, 38, 7 },
{ false, false, StringToInt128("-17014118346046923172246393167902932992"), 38, 6 }}},
// Smallest intermediate value that requires double checking if int256 is required.
{ "cast(9223372036854775808 as decimal(38,0)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, false, StringToInt128("85070591730234615865843651857942052864"), 38, 0 }}},
{ "cast(-9223372036854775808 as decimal(38,0)) * "
"cast(9223372036854775808 as decimal(38,0))",
{{ false, false, StringToInt128("-85070591730234615865843651857942052864"), 38, 0 }}},
// Scale down the intermediate value by 10^39.
{ "cast(0.33333333333333333333333333333333333333 as decimal(38,38)) * "
"cast(0.00000000000000000000000000000000000003 as decimal(38,38))",
{{ false, false, 0, 38, 38 },
{ false, false, 0, 38, 37 }}},
{ "cast(0.33333333333333333333333333333333333333 as decimal(38,38)) * "
"cast(0.3 as decimal(38,38))",
{{ false, false, StringToInt128("9999999999999999999999999999999999999"), 38, 38 },
{ false, false, StringToInt128("1000000000000000000000000000000000000"), 38, 37 }}},
{ "cast(10000000000000000000 as decimal(21,0)) * "
"cast(10000000000000000000 as decimal(21,0))",
{{ false, true, 0, 38, 0 },
{ true, false, 0, 38, 0 }}},
{ "cast(1000000000000000.0000 as decimal(38,4)) * "
"cast(1000000000000000.0000 as decimal(38,4))",
{{ false, true, 0, 38, 8 },
{ false, false, StringToInt128("1000000000000000000000000000000000000"), 38, 6 }}},
{ "cast(-1000000000000000.0000 as decimal(38,4)) * "
"cast(1000000000000000.0000 as decimal(38,4))",
{{ false, true, 0, 38, 8 },
{ false, false, StringToInt128("-1000000000000000000000000000000000000"), 38, 6 }}},
// Smallest 256 bit intermediate result that can't be scaled down to 128 bits.
{ "cast(10000000000000000.0000 as decimal(38,4)) * "
"cast(10000000000000000.0000 as decimal(38,4))",
{{ false, true, 0, 38, 8 },
{ true, false, 0, 38, 6 }}},
{ "cast(-10000000000000000.0000 as decimal(38,4)) * "
"cast(10000000000000000.0000 as decimal(38,4))",
{{ false, true, 0, 38, 8 },
{ true, false, 0, 38, 6 }}},
// The reason why the result of (38,38) * (38,38) is (38,37).
{ "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) * "
"cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
{{ false, false, StringToInt128("99999999999999999999999999999999999998"), 38, 38 },
{ false, false, StringToInt128("10000000000000000000000000000000000000"), 38, 37 }}},
{ "cast(-0.99999999999999999999999999999999999999 as decimal(38,38)) * "
"cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
{{ false, false, StringToInt128("-99999999999999999999999999999999999998"), 38, 38 },
{ false, false, StringToInt128("-10000000000000000000000000000000000000"), 38, 37 }}},
{ "cast(99999999999999999999999999999999999999 as decimal(38,0)) * "
"cast(1 as decimal(38,0))",
{{ false, false, StringToInt128("99999999999999999999999999999999999999"), 38, 0 }}},
{ "cast(99999999999999999999999999999999999999 as decimal(38,0)) * "
"cast(-1 as decimal(38,0))",
{{ false, false, StringToInt128("-99999999999999999999999999999999999999"), 38, 0 }}},
// Rounding.
{ "cast(0.000005 as decimal(38,6)) * cast(0.1 as decimal(38,1))",
{{ false, false, 5, 38, 7 },
{ false, false, 1, 38, 6 }}},
{ "cast(-0.000005 as decimal(38,6)) * cast(0.1 as decimal(38,1))",
{{ false, false, -5, 38, 7 },
{ false, false, -1, 38, 6 }}},
{ "cast(0.000004 as decimal(38,6)) * cast(0.1 as decimal(38,1))",
{{ false, false, 4, 38, 7 },
{ false, false, 0, 38, 6 }}},
{ "cast(-0.000004 as decimal(38,6)) * cast(0.1 as decimal(38,1))",
{{ false, false, -4, 38, 7 },
{ false, false, 0, 38, 6 }}},
// Test divide operator
{ "cast(1.23 as decimal(8,2)) / cast(1 as decimal(4,3))",
{{ false, false, 12300000, 16, 7}}},
{ "cast(1.23 as decimal(30,2)) / cast(1 as decimal(20,3))",
{{ false, false, 1230, 38, 3 },
{ false, false, 12300000, 38, 7 }}},
{ "cast(1 as decimal(38,0)) / cast(.2 as decimal(38,1))",
{{ false, false, 50, 38, 1 },
{ false, false, 5000000, 38, 6 }}},
{ "cast(1 as decimal(38,0)) / cast(3 as decimal(38,0))",
{{ false, false, 0, 38, 0 },
{ false, false, 333333, 38, 6 }}},
{ "cast(99999999999999999999999999999999999999 as decimal(38,0)) / "
"cast(99999999999999999999999999999999999999 as decimal(38,0))",
{{ false, false, 1, 38, 0 },
{ false, false, 1000000, 38, 6 }}},
{ "cast(99999999999999999999999999999999999999 as decimal(38,0)) / "
"cast(0.00000000000000000000000000000000000001 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ true, false, 0, 38, 6 }}},
{ "cast(0.00000000000000000000000000000000000001 as decimal(38,38)) / "
"cast(99999999999999999999999999999999999999 as decimal(38,0))",
{{ false, false, 0, 38, 38 }}},
{ "cast(0.00000000000000000000000000000000000001 as decimal(38,38)) / "
"cast(0.00000000000000000000000000000000000001 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, 1000000, 38, 6 }}},
{ "cast(9999999999999999999.9999999999999999999 as decimal(38,19)) / "
"cast(99999999999999999999999999999.999999999 as decimal(38,9))",
{{ false, false, 1000000000, 38, 19 },
{ false, false, 1, 38, 10 }}},
{ "cast(999999999999999999999999999999999999.99 as decimal(38,2)) / "
"cast(99999999999.999999999999999999999999999 as decimal(38,27))",
{{ false, true, 0, 38, 27 },
{ false, false, static_cast<int128_t>(10) * 10000000000ll *
10000000000ll * 10000000000ll, 38, 6 }}},
{ "cast(-2.12 as decimal(17,2)) / cast(12515.95 as decimal(17,2))",
{{ false, false, -16938386618674571, 37, 20 }}},
{ "cast(-2.12 as decimal(18,2)) / cast(12515.95 as decimal(18,2))",
{{ false, false, 0, 38, 2 },
{ false, false, -16938386618674571, 38, 20 }}},
{ "cast(737373 as decimal(6,0)) / cast(.52525252 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, 1403844764038, 38, 6 }}},
{ "cast(0.000001 as decimal(6,6)) / "
"cast(0.0000000000000000000000000000000000001 as decimal(38,38))",
{{ false, true, 0, 38, 38 },
{ false, false, static_cast<int128_t>(10000000ll) *
10000000000ll * 10000000000ll * 10000000000ll, 38, 6 }}},
{ "cast(98765432109876543210 as decimal(20,0)) / "
"cast(98765432109876543211 as decimal(20,0))",
{{ false, false, 0, 38, 0 },
{ false, false, 1000000000000000000, 38, 18 }}},
{ "cast(111111.1111 as