You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
832 lines
29 KiB
832 lines
29 KiB
/* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "lex_string.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_macros.h"
|
|
#include "my_table_map.h"
|
|
#include "mysys_err.h"
|
|
#include "sql/item.h"
|
|
#include "sql/item_cmpfunc.h"
|
|
#include "sql/item_create.h"
|
|
#include "sql/item_strfunc.h"
|
|
#include "sql/item_timefunc.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_lex.h"
|
|
#include "sql/tztime.h"
|
|
#include "unittest/gunit/fake_table.h"
|
|
#include "unittest/gunit/mock_field_timestamp.h"
|
|
#include "unittest/gunit/test_utils.h"
|
|
|
|
namespace item_unittest {
|
|
|
|
using my_testing::Mock_error_handler;
|
|
using my_testing::Server_initializer;
|
|
using ::testing::Return;
|
|
|
|
class ItemTest : public ::testing::Test {
|
|
protected:
|
|
virtual void SetUp() { initializer.SetUp(); }
|
|
virtual void TearDown() { initializer.TearDown(); }
|
|
|
|
THD *thd() { return initializer.thd(); }
|
|
|
|
Server_initializer initializer;
|
|
};
|
|
|
|
/**
|
|
This is a simple mock Field class, illustrating how to set expectations on
|
|
type_conversion_status Field_long::store(longlong nr, bool unsigned_val);
|
|
*/
|
|
class Mock_field_long : public Field_long {
|
|
public:
|
|
Mock_field_long(uint32 length)
|
|
: Field_long(0, // ptr_arg
|
|
length, // len_arg
|
|
NULL, // null_ptr_arg
|
|
0, // null_bit_arg
|
|
Field::NONE, // auto_flags_arg
|
|
0, // field_name_arg
|
|
false, // zero_arg
|
|
false) // unsigned_arg
|
|
{}
|
|
|
|
// Avoid warning about hiding other overloaded versions of store().
|
|
using Field_long::store;
|
|
|
|
/*
|
|
This is the only member function we need to override.
|
|
Note: Sun Studio needs a little help in resolving longlong.
|
|
*/
|
|
MOCK_METHOD2(store, type_conversion_status(::longlong nr, bool unsigned_val));
|
|
};
|
|
|
|
/**
|
|
Mock class for CHAR field.
|
|
*/
|
|
|
|
class Mock_field_string : public Field_string {
|
|
private:
|
|
Fake_TABLE *m_fake_tbl;
|
|
|
|
public:
|
|
Mock_field_string(uint32 length, const CHARSET_INFO *cs = &my_charset_latin1)
|
|
: Field_string(0, // ptr_arg
|
|
length, // len_arg
|
|
NULL, // null_ptr_arg
|
|
0, // null_bit_arg
|
|
Field::NONE, // auto_flags_arg
|
|
NULL, // field_name_arg
|
|
cs) // char set
|
|
{
|
|
m_fake_tbl = new Fake_TABLE(this);
|
|
|
|
// Allocate place for storing the field value
|
|
ptr = new uchar[length];
|
|
|
|
// Make it possible to write into this field
|
|
bitmap_set_bit(m_fake_tbl->write_set, 0);
|
|
|
|
/*
|
|
check_for_truncated_fields must be set in order for producing
|
|
warning/error for Item_string::save_in_field().
|
|
*/
|
|
m_fake_tbl->in_use->check_for_truncated_fields = CHECK_FIELD_WARN;
|
|
}
|
|
|
|
~Mock_field_string() {
|
|
delete[] ptr;
|
|
ptr = NULL;
|
|
delete m_fake_tbl;
|
|
m_fake_tbl = NULL;
|
|
}
|
|
};
|
|
|
|
/**
|
|
Mock class for VARCHAR field.
|
|
*/
|
|
|
|
class Mock_field_varstring : public Field_varstring {
|
|
private:
|
|
Fake_TABLE *m_fake_tbl;
|
|
|
|
public:
|
|
Mock_field_varstring(uint32 length, TABLE_SHARE *share,
|
|
const CHARSET_INFO *cs = &my_charset_latin1)
|
|
: Field_varstring(length, // len_arg
|
|
false, // maybe_null_arg
|
|
NULL, // field_name_arg
|
|
share, // share
|
|
cs) // char set
|
|
{
|
|
m_fake_tbl = new Fake_TABLE(this);
|
|
|
|
// Allocate place for storing the field value
|
|
ptr = new uchar[length + 1];
|
|
|
|
// Make it possible to write into this field
|
|
bitmap_set_bit(m_fake_tbl->write_set, 0);
|
|
|
|
/*
|
|
check_for_truncated_fields must be set in order for producing
|
|
warning/error for Item_string::save_in_field().
|
|
*/
|
|
m_fake_tbl->in_use->check_for_truncated_fields = CHECK_FIELD_WARN;
|
|
}
|
|
|
|
~Mock_field_varstring() {
|
|
delete[] ptr;
|
|
ptr = NULL;
|
|
delete m_fake_tbl;
|
|
m_fake_tbl = NULL;
|
|
}
|
|
};
|
|
|
|
TEST_F(ItemTest, ItemInt) {
|
|
const int32 val = 42;
|
|
char stringbuf[10];
|
|
(void)snprintf(stringbuf, sizeof(stringbuf), "%d", val);
|
|
|
|
// An Item expects to be owned by current_thd->free_list,
|
|
// so allocate with new, and do not delete it.
|
|
Item_int *item_int = new Item_int(val);
|
|
|
|
EXPECT_EQ(Item::INT_ITEM, item_int->type());
|
|
EXPECT_EQ(INT_RESULT, item_int->result_type());
|
|
EXPECT_EQ(MYSQL_TYPE_LONGLONG, item_int->data_type());
|
|
EXPECT_EQ(val, item_int->val_int());
|
|
EXPECT_DOUBLE_EQ((double)val, item_int->val_real());
|
|
EXPECT_TRUE(item_int->basic_const_item());
|
|
|
|
my_decimal decimal_val;
|
|
EXPECT_EQ(&decimal_val, item_int->val_decimal(&decimal_val));
|
|
|
|
String string_val;
|
|
EXPECT_EQ(&string_val, item_int->val_str(&string_val));
|
|
EXPECT_STREQ(stringbuf, string_val.c_ptr_safe());
|
|
|
|
Mock_field_long field_val(item_int->max_length);
|
|
// We expect to be called with arguments(nr == val, unsigned_val == false)
|
|
EXPECT_CALL(field_val, store(val, false))
|
|
.Times(1)
|
|
.WillRepeatedly(Return(TYPE_OK));
|
|
EXPECT_EQ(TYPE_OK, item_int->save_in_field(&field_val, true));
|
|
|
|
Item *clone = item_int->clone_item();
|
|
EXPECT_TRUE(item_int->eq(clone, true));
|
|
EXPECT_TRUE(item_int->eq(item_int, true));
|
|
|
|
String print_val;
|
|
item_int->print(thd(), &print_val, QT_ORDINARY);
|
|
EXPECT_STREQ(stringbuf, print_val.c_ptr_safe());
|
|
|
|
const uint precision = item_int->decimal_precision();
|
|
EXPECT_EQ(MY_INT32_NUM_DECIMAL_DIGITS, precision);
|
|
|
|
item_int->neg();
|
|
EXPECT_EQ(-val, item_int->val_int());
|
|
EXPECT_EQ(precision - 1, item_int->decimal_precision());
|
|
|
|
// Functions inherited from parent class(es).
|
|
const table_map tmap = 0;
|
|
EXPECT_EQ(tmap, item_int->used_tables());
|
|
|
|
/*
|
|
TODO: There are about 100 member functions in Item.
|
|
Figure out which ones are relevant for unit testing here.
|
|
*/
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemString) {
|
|
const char short_str[] = "abc";
|
|
const char long_str[] = "abcd";
|
|
const char space_str[] = "abc ";
|
|
const char bad_char[] = "𝌆abc";
|
|
const char bad_char_end[] = "abc𝌆";
|
|
|
|
Item_string *item_short_string =
|
|
new Item_string(STRING_WITH_LEN(short_str), &my_charset_latin1);
|
|
Item_string *item_long_string =
|
|
new Item_string(STRING_WITH_LEN(long_str), &my_charset_latin1);
|
|
Item_string *item_space_string =
|
|
new Item_string(STRING_WITH_LEN(space_str), &my_charset_latin1);
|
|
Item_string *item_bad_char =
|
|
new Item_string(STRING_WITH_LEN(bad_char), &my_charset_bin);
|
|
Item_string *item_bad_char_end =
|
|
new Item_string(STRING_WITH_LEN(bad_char_end), &my_charset_bin);
|
|
|
|
/*
|
|
Bug 16407965 ITEM::SAVE_IN_FIELD_NO_WARNING() DOES NOT RETURN CORRECT
|
|
CONVERSION STATUS
|
|
*/
|
|
|
|
// Create a CHAR field that can store short_str but not long_str
|
|
Mock_field_string field_string(3);
|
|
EXPECT_EQ(MYSQL_TYPE_STRING, field_string.type());
|
|
|
|
// CHAR field for testing strings with illegal values
|
|
Mock_field_string field_string_utf8(20, &my_charset_utf8_general_ci);
|
|
EXPECT_EQ(MYSQL_TYPE_STRING, field_string_utf8.type());
|
|
|
|
/*
|
|
Tests of Item_string::save_in_field() when storing into a CHAR field.
|
|
*/
|
|
EXPECT_EQ(TYPE_OK, item_short_string->save_in_field(&field_string, true));
|
|
EXPECT_EQ(TYPE_WARN_TRUNCATED,
|
|
item_long_string->save_in_field(&field_string, true));
|
|
// Field_string does not consider trailing spaces when truncating a string
|
|
EXPECT_EQ(TYPE_OK, item_space_string->save_in_field(&field_string, true));
|
|
// When the first character invalid, the whole string is truncated.
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char->save_in_field(&field_string_utf8, true));
|
|
// If the string contains an invalid character, the entire string is invalid
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char_end->save_in_field(&field_string_utf8, true));
|
|
|
|
/*
|
|
Tests of Item_string::save_in_field_no_warnings() when storing into
|
|
a CHAR field.
|
|
*/
|
|
EXPECT_EQ(TYPE_OK,
|
|
item_short_string->save_in_field_no_warnings(&field_string, true));
|
|
EXPECT_EQ(TYPE_WARN_TRUNCATED,
|
|
item_long_string->save_in_field_no_warnings(&field_string, true));
|
|
// Field_string does not consider trailing spaces when truncating a string
|
|
EXPECT_EQ(TYPE_OK,
|
|
item_space_string->save_in_field_no_warnings(&field_string, true));
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char->save_in_field_no_warnings(&field_string_utf8, true));
|
|
// If the string contains an invalid character, the entire string is invalid
|
|
EXPECT_EQ(
|
|
TYPE_WARN_INVALID_STRING,
|
|
item_bad_char_end->save_in_field_no_warnings(&field_string_utf8, true));
|
|
|
|
/*
|
|
Create a VARCHAR field that can store short_str but not long_str.
|
|
Need a table share object since the constructor for Field_varstring
|
|
updates its table share.
|
|
*/
|
|
TABLE_SHARE table_share;
|
|
Mock_field_varstring field_varstring(3, &table_share);
|
|
EXPECT_EQ(MYSQL_TYPE_VARCHAR, field_varstring.type());
|
|
|
|
// VARCHAR field for testing strings with illegal values
|
|
Mock_field_varstring field_varstring_utf8(20, &table_share,
|
|
&my_charset_utf8_general_ci);
|
|
EXPECT_EQ(MYSQL_TYPE_VARCHAR, field_varstring_utf8.type());
|
|
|
|
/*
|
|
Tests of Item_string::save_in_field() when storing into a VARCHAR field.
|
|
*/
|
|
EXPECT_EQ(TYPE_OK, item_short_string->save_in_field(&field_varstring, true));
|
|
EXPECT_EQ(TYPE_WARN_TRUNCATED,
|
|
item_long_string->save_in_field(&field_varstring, true));
|
|
// Field_varstring produces a note when truncating a string with
|
|
// trailing spaces
|
|
EXPECT_EQ(TYPE_NOTE_TRUNCATED,
|
|
item_space_string->save_in_field(&field_varstring, true));
|
|
// When the first character invalid, the whole string is truncated.
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char->save_in_field(&field_varstring_utf8, true));
|
|
// If the string contains an invalid character, the entire string is invalid
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char_end->save_in_field(&field_varstring_utf8, true));
|
|
|
|
/*
|
|
Tests of Item_string::save_in_field_no_warnings() when storing into
|
|
a VARCHAR field.
|
|
*/
|
|
EXPECT_EQ(TYPE_OK, item_short_string->save_in_field_no_warnings(
|
|
&field_varstring, true));
|
|
EXPECT_EQ(TYPE_WARN_TRUNCATED, item_long_string->save_in_field_no_warnings(
|
|
&field_varstring, true));
|
|
// Field_varstring produces a note when truncating a string with
|
|
// trailing spaces
|
|
EXPECT_EQ(TYPE_NOTE_TRUNCATED, item_space_string->save_in_field_no_warnings(
|
|
&field_varstring, true));
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING, item_bad_char->save_in_field_no_warnings(
|
|
&field_varstring_utf8, true));
|
|
// If the string contains an invalid character, the entire string is invalid
|
|
EXPECT_EQ(TYPE_WARN_INVALID_STRING,
|
|
item_bad_char_end->save_in_field_no_warnings(&field_varstring_utf8,
|
|
true));
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemEqual) {
|
|
// Bug#13720201 VALGRIND: VARIOUS BLOCKS OF BYTES DEFINITELY LOST
|
|
Mock_field_timestamp mft;
|
|
mft.table->const_table = true;
|
|
mft.make_readable();
|
|
// foo is longer than STRING_BUFFER_USUAL_SIZE used by cmp_item_sort_string.
|
|
const char foo[] =
|
|
"0123456789012345678901234567890123456789"
|
|
"0123456789012345678901234567890123456789"
|
|
"0123456789012345678901234567890123456789";
|
|
Item_equal *item_equal =
|
|
new Item_equal(new Item_string(STRING_WITH_LEN(foo), &my_charset_bin),
|
|
new Item_field(&mft));
|
|
|
|
EXPECT_FALSE(item_equal->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(1, item_equal->val_int());
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemFuncExportSet) {
|
|
String str;
|
|
Item *on_string = new Item_string(STRING_WITH_LEN("on"), &my_charset_bin);
|
|
Item *off_string = new Item_string(STRING_WITH_LEN("off"), &my_charset_bin);
|
|
Item *sep_string = new Item_string(STRING_WITH_LEN(","), &my_charset_bin);
|
|
{
|
|
// Testing basic functionality.
|
|
Item *export_set =
|
|
new Item_func_export_set(POS(), new Item_int(2), on_string, off_string,
|
|
sep_string, new Item_int(4));
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(&str, export_set->val_str(&str));
|
|
EXPECT_STREQ("off,on,off,off", str.c_ptr_safe());
|
|
}
|
|
{
|
|
// Testing corner case: number_of_bits == zero.
|
|
Item *export_set =
|
|
new Item_func_export_set(POS(), new Item_int(2), on_string, off_string,
|
|
sep_string, new Item_int(0));
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(&str, export_set->val_str(&str));
|
|
EXPECT_STREQ("", str.c_ptr_safe());
|
|
}
|
|
|
|
/*
|
|
Bug#11765562 58545:
|
|
EXPORT_SET() CAN BE USED TO MAKE ENTIRE SERVER COMPLETELY UNRESPONSIVE
|
|
*/
|
|
const ulong max_size = 1024;
|
|
const ulonglong repeat = max_size / 2;
|
|
Item *item_int_repeat = new Item_int(repeat);
|
|
Item *string_x = new Item_string(STRING_WITH_LEN("x"), &my_charset_bin);
|
|
String *const null_string = NULL;
|
|
thd()->variables.max_allowed_packet = max_size;
|
|
{
|
|
// Testing overflow caused by 'on-string'.
|
|
Mock_error_handler error_handler(thd(), ER_WARN_ALLOWED_PACKET_OVERFLOWED);
|
|
Item *export_set = new Item_func_export_set(
|
|
POS(), new Item_int(0xff),
|
|
new Item_func_repeat(POS(), string_x, item_int_repeat), string_x,
|
|
sep_string);
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
SCOPED_TRACE("");
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(null_string, export_set->val_str(&str));
|
|
EXPECT_STREQ("", str.c_ptr_safe());
|
|
EXPECT_EQ(1, error_handler.handle_called());
|
|
}
|
|
{
|
|
// Testing overflow caused by 'off-string'.
|
|
Mock_error_handler error_handler(thd(), ER_WARN_ALLOWED_PACKET_OVERFLOWED);
|
|
Item *export_set = new Item_func_export_set(
|
|
POS(), new Item_int(0xff), string_x,
|
|
new Item_func_repeat(POS(), string_x, item_int_repeat), sep_string);
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
SCOPED_TRACE("");
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(null_string, export_set->val_str(&str));
|
|
EXPECT_STREQ("", str.c_ptr_safe());
|
|
EXPECT_EQ(1, error_handler.handle_called());
|
|
}
|
|
{
|
|
// Testing overflow caused by 'separator-string'.
|
|
Mock_error_handler error_handler(thd(), ER_WARN_ALLOWED_PACKET_OVERFLOWED);
|
|
Item *export_set = new Item_func_export_set(
|
|
POS(), new Item_int(0xff), string_x, string_x,
|
|
new Item_func_repeat(POS(), string_x, item_int_repeat));
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
SCOPED_TRACE("");
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(null_string, export_set->val_str(&str));
|
|
EXPECT_STREQ("", str.c_ptr_safe());
|
|
EXPECT_EQ(1, error_handler.handle_called());
|
|
}
|
|
{
|
|
// Testing overflow caused by 'on-string'.
|
|
longlong max_size = 1024LL;
|
|
thd()->variables.max_allowed_packet = static_cast<ulong>(max_size);
|
|
Mock_error_handler error_handler(thd(), ER_WARN_ALLOWED_PACKET_OVERFLOWED);
|
|
Item *lpad = new Item_func_lpad(
|
|
POS(), new Item_string(STRING_WITH_LEN("a"), &my_charset_bin),
|
|
new Item_int(max_size),
|
|
new Item_string(STRING_WITH_LEN("pppppppppppppppp"
|
|
"pppppppppppppppp"),
|
|
&my_charset_bin));
|
|
Item *export_set = new Item_func_export_set(
|
|
POS(), new Item_string(STRING_WITH_LEN("1111111"), &my_charset_bin),
|
|
lpad, new Item_int(1));
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
SCOPED_TRACE("");
|
|
EXPECT_FALSE(export_set->itemize(&pc, &export_set));
|
|
EXPECT_FALSE(export_set->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(null_string, export_set->val_str(&str));
|
|
EXPECT_STREQ("", str.c_ptr_safe());
|
|
EXPECT_EQ(1, error_handler.handle_called());
|
|
}
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemFuncIntDivOverflow) {
|
|
const char dividend_str[] =
|
|
"99999999999999999999999999999999999999999"
|
|
"99999999999999999999999999999999999999999";
|
|
const char divisor_str[] = "0.5";
|
|
Item_float *dividend = new Item_float(dividend_str, sizeof(dividend_str));
|
|
Item_float *divisor = new Item_float(divisor_str, sizeof(divisor_str));
|
|
Item_func_int_div *quotient = new Item_func_int_div(dividend, divisor);
|
|
|
|
Mock_error_handler error_handler(thd(), ER_TRUNCATED_WRONG_VALUE);
|
|
EXPECT_FALSE(quotient->fix_fields(thd(), NULL));
|
|
initializer.set_expected_error(ER_DATA_OUT_OF_RANGE);
|
|
quotient->val_int();
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemFuncIntDivUnderflow) {
|
|
// Bug #11792200 - DIVIDING LARGE NUMBERS CAUSES STACK CORRUPTIONS
|
|
const char dividend_str[] = "1.175494351E-37";
|
|
const char divisor_str[] = "1.7976931348623157E+308";
|
|
Item_float *dividend = new Item_float(dividend_str, sizeof(dividend_str));
|
|
Item_float *divisor = new Item_float(divisor_str, sizeof(divisor_str));
|
|
Item_func_int_div *quotient = new Item_func_int_div(dividend, divisor);
|
|
|
|
Mock_error_handler error_handler(thd(), ER_TRUNCATED_WRONG_VALUE);
|
|
EXPECT_FALSE(quotient->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(0, quotient->val_int());
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemFuncNegLongLongMin) {
|
|
// Bug#14314156 MAIN.FUNC_MATH TEST FAILS ON MYSQL-TRUNK ON PB2
|
|
const longlong longlong_min = LLONG_MIN;
|
|
Item_func_neg *item_neg = new Item_func_neg(new Item_int(longlong_min));
|
|
|
|
EXPECT_FALSE(item_neg->fix_fields(thd(), NULL));
|
|
initializer.set_expected_error(ER_DATA_OUT_OF_RANGE);
|
|
EXPECT_EQ(0, item_neg->int_op());
|
|
}
|
|
|
|
/*
|
|
This is not an exhaustive test. It simply demonstrates that more of the
|
|
initializations in mysqld.cc are needed for testing Item_xxx classes.
|
|
*/
|
|
TEST_F(ItemTest, ItemFuncSetUserVar) {
|
|
const longlong val1 = 1;
|
|
Item_decimal *item_dec = new Item_decimal(val1, false);
|
|
Item_string *item_str = new Item_string("1", 1, &my_charset_latin1);
|
|
|
|
LEX_CSTRING var_name = {STRING_WITH_LEN("a")};
|
|
Item_func_set_user_var *user_var =
|
|
new Item_func_set_user_var(var_name, item_str, false);
|
|
EXPECT_FALSE(user_var->set_entry(thd(), true));
|
|
EXPECT_FALSE(user_var->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(val1, user_var->val_int());
|
|
|
|
my_decimal decimal;
|
|
my_decimal *decval_1 = user_var->val_decimal(&decimal);
|
|
user_var->save_item_result(item_str);
|
|
my_decimal *decval_2 = user_var->val_decimal(&decimal);
|
|
user_var->save_item_result(item_dec);
|
|
|
|
EXPECT_EQ(decval_1, decval_2);
|
|
EXPECT_EQ(decval_1, &decimal);
|
|
}
|
|
|
|
// Test of Item::operator new() when we simulate out-of-memory.
|
|
TEST_F(ItemTest, OutOfMemory) {
|
|
Item_int *item = new Item_int(42);
|
|
EXPECT_NE(nullptr, item);
|
|
|
|
#if !defined(DBUG_OFF)
|
|
// Setting debug flags triggers enter/exit trace, so redirect to /dev/null.
|
|
DBUG_SET("o," IF_WIN("NUL", "/dev/null"));
|
|
|
|
DBUG_SET("+d,simulate_out_of_memory");
|
|
initializer.set_expected_error(EE_OUTOFMEMORY);
|
|
item = new Item_int(42);
|
|
EXPECT_EQ(nullptr, item);
|
|
|
|
DBUG_SET("+d,simulate_out_of_memory");
|
|
item = new (thd()->mem_root) Item_int(42);
|
|
EXPECT_EQ(nullptr, item);
|
|
#endif
|
|
}
|
|
|
|
// We never use dynamic_cast, but we expect it to work.
|
|
TEST_F(ItemTest, DynamicCast) {
|
|
Item *item = new Item_int(42);
|
|
const Item_int *null_item = NULL;
|
|
EXPECT_NE(null_item, dynamic_cast<Item_int *>(item));
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemFuncXor) {
|
|
const uint length = 1U;
|
|
Item_int *item_zero = new Item_int(0, length);
|
|
Item_int *item_one_a = new Item_int(1, length);
|
|
|
|
Item_func_xor *item_xor = new Item_func_xor(item_zero, item_one_a);
|
|
|
|
EXPECT_FALSE(item_xor->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(1, item_xor->val_int());
|
|
EXPECT_EQ(1U, item_xor->decimal_precision());
|
|
|
|
Item_int *item_one_b = new Item_int(1, length);
|
|
|
|
Item_func_xor *item_xor_same = new Item_func_xor(item_one_a, item_one_b);
|
|
|
|
EXPECT_FALSE(item_xor_same->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(0, item_xor_same->val_int());
|
|
EXPECT_FALSE(item_xor_same->val_bool());
|
|
EXPECT_FALSE(item_xor_same->is_null());
|
|
|
|
String print_buffer;
|
|
item_xor->print(thd(), &print_buffer, QT_ORDINARY);
|
|
EXPECT_STREQ("(0 xor 1)", print_buffer.c_ptr_safe());
|
|
|
|
Item *neg_xor = item_xor->truth_transformer(thd(), Item::BOOL_NEGATED);
|
|
EXPECT_FALSE(neg_xor->fix_fields(thd(), NULL));
|
|
EXPECT_EQ(0, neg_xor->val_int());
|
|
EXPECT_DOUBLE_EQ(0.0, neg_xor->val_real());
|
|
EXPECT_FALSE(neg_xor->val_bool());
|
|
EXPECT_FALSE(neg_xor->is_null());
|
|
|
|
print_buffer = String();
|
|
neg_xor->print(thd(), &print_buffer, QT_ORDINARY);
|
|
EXPECT_STREQ("((not(0)) xor 1)", print_buffer.c_ptr_safe());
|
|
|
|
Item_func_xor *item_xor_null = new Item_func_xor(item_zero, new Item_null());
|
|
EXPECT_FALSE(item_xor_null->fix_fields(thd(), NULL));
|
|
|
|
EXPECT_EQ(0, item_xor_null->val_int());
|
|
EXPECT_TRUE(item_xor_null->is_null());
|
|
}
|
|
|
|
/*
|
|
Testing MYSQL_TIME_cache.
|
|
*/
|
|
TEST_F(ItemTest, MysqlTimeCache) {
|
|
String str_buff, *str;
|
|
MYSQL_TIME datetime6 = {
|
|
2011, 11, 7, 10, 20, 30, 123456, 0, MYSQL_TIMESTAMP_DATETIME};
|
|
MYSQL_TIME time6 = {0, 0, 0, 10, 20, 30, 123456, 0, MYSQL_TIMESTAMP_TIME};
|
|
struct timeval tv6 = {1320661230, 123456};
|
|
const MYSQL_TIME *ltime;
|
|
MYSQL_TIME_cache cache;
|
|
|
|
/*
|
|
Testing DATETIME(6).
|
|
Initializing from MYSQL_TIME.
|
|
*/
|
|
cache.set_datetime(&datetime6, 6);
|
|
EXPECT_EQ(1840440237558456896LL, cache.val_packed());
|
|
EXPECT_EQ(6, cache.decimals());
|
|
// Call val_str() then cptr()
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", str->c_ptr_safe());
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", cache.cptr());
|
|
cache.set_datetime(&datetime6, 6);
|
|
// Now call the other way around: cptr() then val_str()
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", cache.cptr());
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", str->c_ptr_safe());
|
|
// Testing get_TIME_ptr()
|
|
ltime = cache.get_TIME_ptr();
|
|
EXPECT_EQ(ltime->year, datetime6.year);
|
|
EXPECT_EQ(ltime->month, datetime6.month);
|
|
EXPECT_EQ(ltime->day, datetime6.day);
|
|
EXPECT_EQ(ltime->hour, datetime6.hour);
|
|
EXPECT_EQ(ltime->minute, datetime6.minute);
|
|
EXPECT_EQ(ltime->second, datetime6.second);
|
|
EXPECT_EQ(ltime->second_part, datetime6.second_part);
|
|
EXPECT_EQ(ltime->neg, datetime6.neg);
|
|
EXPECT_EQ(ltime->time_type, datetime6.time_type);
|
|
// Testing eq()
|
|
{
|
|
MYSQL_TIME datetime6_2 = datetime6;
|
|
MYSQL_TIME_cache cache2;
|
|
datetime6_2.second_part += 1;
|
|
cache2.set_datetime(&datetime6_2, 6);
|
|
EXPECT_EQ(cache.eq(cache), true);
|
|
EXPECT_EQ(cache.eq(cache2), false);
|
|
EXPECT_EQ(cache2.eq(cache2), true);
|
|
EXPECT_EQ(cache2.eq(cache), false);
|
|
}
|
|
|
|
/*
|
|
Testing DATETIME(6).
|
|
Initializing from "struct timeval".
|
|
*/
|
|
cache.set_datetime(tv6, 6, my_tz_UTC);
|
|
EXPECT_EQ(1840440237558456896LL, cache.val_packed());
|
|
EXPECT_EQ(6, cache.decimals());
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", str->c_ptr_safe());
|
|
EXPECT_STREQ("2011-11-07 10:20:30.123456", cache.cptr());
|
|
|
|
/*
|
|
Testing TIME(6).
|
|
Initializing from MYSQL_TIME.
|
|
*/
|
|
cache.set_time(&time6, 6);
|
|
EXPECT_EQ(709173043776LL, cache.val_packed());
|
|
EXPECT_EQ(6, cache.decimals());
|
|
// Call val_str() then cptr()
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("10:20:30.123456", str->c_ptr_safe());
|
|
EXPECT_STREQ("10:20:30.123456", cache.cptr());
|
|
|
|
/*
|
|
Testing TIME(6).
|
|
Initializing from "struct timeval".
|
|
*/
|
|
cache.set_time(tv6, 6, my_tz_UTC);
|
|
EXPECT_EQ(709173043776LL, cache.val_packed());
|
|
EXPECT_EQ(6, cache.decimals());
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("10:20:30.123456", str->c_ptr_safe());
|
|
EXPECT_STREQ("10:20:30.123456", cache.cptr());
|
|
|
|
/*
|
|
Testing DATETIME(5)
|
|
*/
|
|
MYSQL_TIME datetime5 = {
|
|
2011, 11, 7, 10, 20, 30, 123450, 0, MYSQL_TIMESTAMP_DATETIME};
|
|
cache.set_datetime(&datetime5, 5);
|
|
EXPECT_EQ(1840440237558456890LL, cache.val_packed());
|
|
EXPECT_EQ(5, cache.decimals());
|
|
/* Call val_str() then cptr() */
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("2011-11-07 10:20:30.12345", str->c_ptr_safe());
|
|
EXPECT_STREQ("2011-11-07 10:20:30.12345", cache.cptr());
|
|
cache.set_datetime(&datetime5, 5);
|
|
/* Now call the other way around: cptr() then val_str() */
|
|
EXPECT_STREQ("2011-11-07 10:20:30.12345", cache.cptr());
|
|
EXPECT_STREQ("2011-11-07 10:20:30.12345", str->c_ptr_safe());
|
|
|
|
/*
|
|
Testing DATE.
|
|
Initializing from MYSQL_TIME.
|
|
*/
|
|
MYSQL_TIME date = {2011, 11, 7, 0, 0, 0, 0, 0, MYSQL_TIMESTAMP_DATE};
|
|
cache.set_date(&date);
|
|
EXPECT_EQ(1840439528385413120LL, cache.val_packed());
|
|
EXPECT_EQ(0, cache.decimals());
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("2011-11-07", str->c_ptr_safe());
|
|
EXPECT_STREQ("2011-11-07", cache.cptr());
|
|
|
|
/*
|
|
Testing DATE.
|
|
Initializing from "struct tm".
|
|
*/
|
|
cache.set_date(tv6, my_tz_UTC);
|
|
EXPECT_EQ(1840439528385413120LL, cache.val_packed());
|
|
EXPECT_EQ(0, cache.decimals());
|
|
str = cache.val_str(&str_buff);
|
|
EXPECT_STREQ("2011-11-07", str->c_ptr_safe());
|
|
EXPECT_STREQ("2011-11-07", cache.cptr());
|
|
}
|
|
|
|
extern "C" {
|
|
// Verifies that Item_func_conv::val_str does not call my_strntoll()
|
|
longlong fail_strntoll(const CHARSET_INFO *, const char *, size_t, int,
|
|
const char **, int *) {
|
|
ADD_FAILURE() << "Unexpected call";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
class Mock_charset : public CHARSET_INFO {
|
|
public:
|
|
Mock_charset(const CHARSET_INFO &csi) {
|
|
CHARSET_INFO *this_as_cset = this;
|
|
*this_as_cset = csi;
|
|
|
|
number = 666;
|
|
m_cset_handler = *(csi.cset);
|
|
m_cset_handler.strntoll = fail_strntoll;
|
|
cset = &m_cset_handler;
|
|
}
|
|
|
|
private:
|
|
MY_CHARSET_HANDLER m_cset_handler;
|
|
};
|
|
|
|
TEST_F(ItemTest, ItemFuncConvIntMin) {
|
|
Mock_charset charset(*system_charset_info);
|
|
SCOPED_TRACE("");
|
|
Item *item_conv = new Item_func_conv(POS(), new Item_string("5", 1, &charset),
|
|
new Item_int(INT_MIN), // from_base
|
|
new Item_int(INT_MIN)); // to_base
|
|
Parse_context pc(thd(), thd()->lex->current_select());
|
|
EXPECT_FALSE(item_conv->itemize(&pc, &item_conv));
|
|
EXPECT_FALSE(item_conv->fix_fields(thd(), NULL));
|
|
const String *null_string = NULL;
|
|
String str;
|
|
EXPECT_EQ(null_string, item_conv->val_str(&str));
|
|
}
|
|
|
|
TEST_F(ItemTest, ItemDecimalTypecast) {
|
|
const char msg[] = "";
|
|
POS pos;
|
|
pos.cpp.start = pos.cpp.end = pos.raw.start = pos.raw.end = msg;
|
|
// Sun Studio needs this null_item,
|
|
// it fails to compile EXPECT_EQ(NULL, create_func_cast());
|
|
const Item *null_item = NULL;
|
|
|
|
Cast_type type;
|
|
type.target = ITEM_CAST_DECIMAL;
|
|
|
|
type.length = "123456789012345678901234567890";
|
|
type.dec = NULL;
|
|
|
|
{
|
|
initializer.set_expected_error(ER_TOO_BIG_PRECISION);
|
|
EXPECT_EQ(null_item, create_func_cast(thd(), pos, NULL, &type));
|
|
}
|
|
|
|
{
|
|
char buff[20];
|
|
snprintf(buff, sizeof(buff) - 1, "%d", DECIMAL_MAX_PRECISION + 1);
|
|
type.length = buff;
|
|
type.dec = NULL;
|
|
initializer.set_expected_error(ER_TOO_BIG_PRECISION);
|
|
EXPECT_EQ(null_item, create_func_cast(thd(), pos, NULL, &type));
|
|
}
|
|
|
|
{
|
|
type.length = NULL;
|
|
type.dec = "123456789012345678901234567890";
|
|
initializer.set_expected_error(ER_TOO_BIG_SCALE);
|
|
EXPECT_EQ(null_item, create_func_cast(thd(), pos, NULL, &type));
|
|
}
|
|
|
|
{
|
|
char buff[20];
|
|
snprintf(buff, sizeof(buff) - 1, "%d", DECIMAL_MAX_SCALE + 1);
|
|
type.length = buff;
|
|
type.dec = buff;
|
|
initializer.set_expected_error(ER_TOO_BIG_SCALE);
|
|
EXPECT_EQ(null_item, create_func_cast(thd(), pos, NULL, &type));
|
|
}
|
|
}
|
|
|
|
TEST_F(ItemTest, NormalizedPrint) {
|
|
Item_null *item_null = new Item_null;
|
|
{
|
|
String s;
|
|
item_null->print(thd(), &s, QT_ORDINARY);
|
|
EXPECT_STREQ("NULL", s.c_ptr());
|
|
}
|
|
{
|
|
String s;
|
|
item_null->print(thd(), &s, QT_NORMALIZED_FORMAT);
|
|
EXPECT_STREQ("?", s.c_ptr());
|
|
}
|
|
}
|
|
|
|
TEST_F(ItemTest, CompareEmptyStrings) {
|
|
Item *item1 = new Item_string(nullptr, 0, &my_charset_bin);
|
|
Item *item2 = new Item_string(nullptr, 0, &my_charset_bin);
|
|
Item_result_field *owner = new Item_func_le(item1, item2);
|
|
EXPECT_FALSE(item1->fix_fields(thd(), nullptr));
|
|
EXPECT_FALSE(item2->fix_fields(thd(), nullptr));
|
|
|
|
Arg_comparator comparator(&item1, &item2);
|
|
comparator.set_cmp_func(owner, &item1, &item2, false);
|
|
|
|
EXPECT_EQ(0, comparator.compare_binary_string());
|
|
}
|
|
|
|
} // namespace item_unittest
|
|
|