Commit 0e8544cd authored by Alexander Barkov's avatar Alexander Barkov

MDEV-29355 Backport templatized INET6 implementation from 10.7 to 10.6

parent 4feb9df1
......@@ -158,7 +158,7 @@ String *Item_func_inet6_aton::val_str(String *buffer)
return buffer;
}
Inet6_null ipv6(*tmp.string());
Inet6Bundle::Fbt_null ipv6(*tmp.string());
if (!ipv6.is_null())
{
ipv6.to_binary(buffer);
......@@ -197,7 +197,7 @@ String *Item_func_inet6_ntoa::val_str_ascii(String *buffer)
return buffer;
}
Inet6_null ipv6(static_cast<const Binary_string&>(*tmp.string()));
Inet6Bundle::Fbt_null ipv6(static_cast<const Binary_string&>(*tmp.string()));
if (!ipv6.is_null())
{
ipv6.to_string(buffer);
......@@ -221,6 +221,22 @@ longlong Item_func_is_ipv4::val_int()
return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null();
}
class IP6 : public Inet6Bundle::Fbt_null
{
public:
IP6(Item* arg) : Inet6Bundle::Fbt_null(arg) {}
bool is_v4compat() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer);
}
bool is_v4mapped() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer);
}
};
/**
Checks if the passed string represents an IPv6-address.
......@@ -230,17 +246,16 @@ longlong Item_func_is_ipv6::val_int()
{
DBUG_ASSERT(fixed());
String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
return !tmp.is_null() && !Inet6_null(*tmp.string()).is_null();
return !tmp.is_null() && !Inet6Bundle::Fbt_null(*tmp.string()).is_null();
}
/**
Checks if the passed IPv6-address is an IPv4-compat IPv6-address.
*/
longlong Item_func_is_ipv4_compat::val_int()
{
Inet6_null ip6(args[0]);
IP6 ip6(args[0]);
return !ip6.is_null() && ip6.is_v4compat();
}
......@@ -251,6 +266,6 @@ longlong Item_func_is_ipv4_compat::val_int()
longlong Item_func_is_ipv4_mapped::val_int()
{
Inet6_null ip6(args[0]);
IP6 ip6(args[0]);
return !ip6.is_null() && ip6.is_v4mapped();
}
......@@ -2229,3 +2229,31 @@ SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1);
d
12::
DROP TABLE t1;
#
# MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle<FbtImpl>::Fbt FixedBinTypeBundle<FbtImpl>::Field_fbt::to_fbt()
#
CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10');
INSERT INTO t1(id) VALUES (1), (2), (3), (4);
INSERT INTO t1 VALUES (5,'::5'), (6,'::6');
SELECT * FROM t1 ORDER BY a;
id a
5 ::5
6 ::6
1 ::10
2 ::10
3 ::10
4 ::10
CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a;
CREATE TABLE t2 SELECT * FROM v1;
SELECT * FROM v1 ORDER BY a;
a m
::5 5
::6 6
::10 1
SELECT * FROM t2 ORDER BY a;
a m
::5 5
::6 6
::10 1
DROP VIEW v1;
DROP TABLE t1, t2;
......@@ -1641,3 +1641,18 @@ SELECT * FROM t1 ORDER BY d;
SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1);
SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1);
DROP TABLE t1;
--echo #
--echo # MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle<FbtImpl>::Fbt FixedBinTypeBundle<FbtImpl>::Field_fbt::to_fbt()
--echo #
CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10');
INSERT INTO t1(id) VALUES (1), (2), (3), (4);
INSERT INTO t1 VALUES (5,'::5'), (6,'::6');
SELECT * FROM t1 ORDER BY a;
CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a;
CREATE TABLE t2 SELECT * FROM v1;
SELECT * FROM v1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
DROP VIEW v1;
DROP TABLE t1, t2;
/* Copyright (c) 2019 MariaDB Corporation
/* Copyright (c) 2019,2021 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -21,14 +21,10 @@
#include <mysql/plugin_data_type.h>
#include <mysql/plugin_function.h>
Type_handler_inet6 type_handler_inet6;
static struct st_mariadb_data_type plugin_descriptor_type_inet6=
{
MariaDB_DATA_TYPE_INTERFACE_VERSION,
&type_handler_inet6
Inet6Bundle::type_handler_fbt()
};
......
/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2014 MariaDB Foundation
Copyright (c) 2019 MariaDB Corporation
Copyright (c) 2019,2022 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -168,7 +168,7 @@ bool Inet4::ascii_to_ipv4(const char *str, size_t str_length)
IPv4-part differently on different platforms.
*/
bool Inet6::ascii_to_ipv6(const char *str, size_t str_length)
bool Inet6::ascii_to_fbt(const char *str, size_t str_length)
{
if (str_length < 2)
{
......@@ -507,1203 +507,8 @@ size_t Inet6::to_string(char *dst, size_t dstsize) const
return (size_t) (p - dst);
}
bool Inet6::fix_fields_maybe_null_on_conversion_to_inet6(Item *item)
{
if (item->maybe_null())
return true;
if (item->type_handler() == &type_handler_inet6)
return false;
if (!item->const_item() || item->is_expensive())
return true;
return Inet6_null(item, false).is_null();
}
bool Inet6::make_from_item(Item *item, bool warn)
{
if (item->type_handler() == &type_handler_inet6)
{
Native tmp(m_buffer, sizeof(m_buffer));
bool rc= item->val_native(current_thd, &tmp);
if (rc)
return true;
DBUG_ASSERT(tmp.length() == sizeof(m_buffer));
if (tmp.ptr() != m_buffer)
memcpy(m_buffer, tmp.ptr(), sizeof(m_buffer));
return false;
}
StringBufferInet6 tmp;
String *str= item->val_str(&tmp);
return str ? make_from_character_or_binary_string(str, warn) : true;
}
bool Inet6::make_from_character_or_binary_string(const String *str, bool warn)
{
static Name name= type_handler_inet6.name();
if (str->charset() != &my_charset_bin)
{
bool rc= character_string_to_ipv6(str->ptr(), str->length(),
str->charset());
if (rc && warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(),
ErrConvString(str).ptr());
return rc;
}
if (str->length() != sizeof(m_buffer))
{
if (warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name.ptr(),
ErrConvString(str).ptr());
return true;
}
DBUG_ASSERT(str->ptr() != m_buffer);
memcpy(m_buffer, str->ptr(), sizeof(m_buffer));
return false;
};
/********************************************************************/
class cmp_item_inet6: public cmp_item_scalar
{
Inet6 m_native;
public:
cmp_item_inet6()
:cmp_item_scalar(),
m_native(Inet6_zero())
{ }
void store_value(Item *item) override
{
m_native= Inet6(item, &m_null_value);
}
int cmp_not_null(const Value *val) override
{
DBUG_ASSERT(!val->is_null());
DBUG_ASSERT(val->is_string());
Inet6_null tmp(val->m_string);
DBUG_ASSERT(!tmp.is_null());
return m_native.cmp(tmp);
}
int cmp(Item *arg) override
{
Inet6_null tmp(arg);
return m_null_value || tmp.is_null() ? UNKNOWN : m_native.cmp(tmp) != 0;
}
int compare(cmp_item *ci) override
{
cmp_item_inet6 *tmp= static_cast<cmp_item_inet6*>(ci);
DBUG_ASSERT(!m_null_value);
DBUG_ASSERT(!tmp->m_null_value);
return m_native.cmp(tmp->m_native);
}
cmp_item *make_same(THD *thd) override
{
return new (thd->mem_root) cmp_item_inet6();
}
};
class Field_inet6: public Field
{
static void set_min_value(char *ptr)
{
memset(ptr, 0, Inet6::binary_length());
}
static void set_max_value(char *ptr)
{
memset(ptr, 0xFF, Inet6::binary_length());
}
void store_warning(const ErrConv &str,
Sql_condition::enum_warning_level level)
{
static const Name type_name= type_handler_inet6.name();
if (get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION)
return;
const TABLE_SHARE *s= table->s;
get_thd()->push_warning_truncated_value_for_field(level, type_name.ptr(),
str.ptr(),
s ? s->db.str : nullptr,
s ? s->table_name.str
: nullptr,
field_name.str);
}
int set_null_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_null();
return 1;
}
int set_min_value_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_min_value((char*) ptr);
return 1;
}
int set_max_value_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_max_value((char*) ptr);
return 1;
}
int store_inet6_null_with_warn(const Inet6_null &inet6,
const ErrConvString &err)
{
DBUG_ASSERT(marked_for_write_or_computed());
if (inet6.is_null())
return maybe_null() ? set_null_with_warn(err) :
set_min_value_with_warn(err);
inet6.to_binary((char *) ptr, Inet6::binary_length());
return 0;
}
public:
Field_inet6(const LEX_CSTRING *field_name_arg, const Record_addr &rec)
:Field(rec.ptr(), Inet6::max_char_length(),
rec.null_ptr(), rec.null_bit(), Field::NONE, field_name_arg)
{
flags|= BINARY_FLAG | UNSIGNED_FLAG;
}
const Type_handler *type_handler() const override
{
return &type_handler_inet6;
}
uint32 max_display_length() const override { return field_length; }
bool str_needs_quotes() const override { return true; }
const DTCollation &dtcollation() const override
{
static DTCollation_numeric c;
return c;
}
CHARSET_INFO *charset(void) const override { return &my_charset_numeric; }
const CHARSET_INFO *sort_charset(void) const override { return &my_charset_bin; }
/**
This makes client-server protocol convert the value according
to @@character_set_client.
*/
bool binary() const override { return false; }
enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
bool is_equal(const Column_definition &new_field) const override
{
return new_field.type_handler() == type_handler();
}
bool eq_def(const Field *field) const override
{
return Field::eq_def(field);
}
double pos_in_interval(Field *min, Field *max) override
{
return pos_in_interval_val_str(min, max, 0);
}
int cmp(const uchar *a, const uchar *b) const override
{ return memcmp(a, b, pack_length()); }
void sort_string(uchar *to, uint length) override
{
DBUG_ASSERT(length == pack_length());
memcpy(to, ptr, length);
}
uint32 pack_length() const override
{
return Inet6::binary_length();
}
uint pack_length_from_metadata(uint field_metadata) const override
{
return Inet6::binary_length();
}
void sql_type(String &str) const override
{
static Name name= type_handler_inet6.name();
str.set_ascii(name.ptr(), name.length());
}
void make_send_field(Send_field *to) override
{
Field::make_send_field(to);
to->set_data_type_name(type_handler_inet6.name().lex_cstring());
}
bool validate_value_in_record(THD *thd, const uchar *record) const override
{
return false;
}
String *val_str(String *val_buffer,
String *val_ptr __attribute__((unused))) override
{
DBUG_ASSERT(marked_for_read());
Inet6_null tmp((const char *) ptr, pack_length());
return tmp.to_string(val_buffer) ? NULL : val_buffer;
}
my_decimal *val_decimal(my_decimal *to) override
{
DBUG_ASSERT(marked_for_read());
my_decimal_set_zero(to);
return to;
}
longlong val_int() override
{
DBUG_ASSERT(marked_for_read());
return 0;
}
double val_real() override
{
DBUG_ASSERT(marked_for_read());
return 0;
}
bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
DBUG_ASSERT(marked_for_read());
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_bool(void) override
{
DBUG_ASSERT(marked_for_read());
return !Inet6::only_zero_bytes((const char *) ptr, Inet6::binary_length());
}
int store_native(const Native &value) override
{
DBUG_ASSERT(marked_for_write_or_computed());
DBUG_ASSERT(value.length() == Inet6::binary_length());
memcpy(ptr, value.ptr(), value.length());
return 0;
}
int store(const char *str, size_t length, CHARSET_INFO *cs) override
{
return cs == &my_charset_bin ? store_binary(str, length) :
store_text(str, length, cs);
}
int store_text(const char *str, size_t length, CHARSET_INFO *cs) override
{
return store_inet6_null_with_warn(Inet6_null(str, length, cs),
ErrConvString(str, length, cs));
}
int store_binary(const char *str, size_t length) override
{
return store_inet6_null_with_warn(Inet6_null(str, length),
ErrConvString(str, length,
&my_charset_bin));
}
int store_hex_hybrid(const char *str, size_t length) override
{
return Field_inet6::store_binary(str, length);
}
int store_decimal(const my_decimal *num) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvDecimal(num));
}
int store(longlong nr, bool unsigned_flag) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(
ErrConvInteger(Longlong_hybrid(nr, unsigned_flag)));
}
int store(double nr) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvDouble(nr));
}
int store_time_dec(const MYSQL_TIME *ltime, uint dec) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvTime(ltime));
}
/*** Field conversion routines ***/
int store_field(Field *from) override
{
// INSERT INTO t1 (inet6_field) SELECT different_field_type FROM t2;
return from->save_in_field(this);
}
int save_in_field(Field *to) override
{
// INSERT INTO t2 (different_field_type) SELECT inet6_field FROM t1;
if (to->charset() == &my_charset_bin &&
dynamic_cast<const Type_handler_general_purpose_string*>
(to->type_handler()))
{
NativeBufferInet6 res;
val_native(&res);
return to->store(res.ptr(), res.length(), &my_charset_bin);
}
return save_in_field_str(to);
}
Copy_func *get_copy_func(const Field *from) const override
{
// ALTER to INET6 from another field
return do_field_string;
}
Copy_func *get_copy_func_to(const Field *to) const override
{
if (type_handler() == to->type_handler())
{
// ALTER from INET6 to INET6
DBUG_ASSERT(pack_length() == to->pack_length());
DBUG_ASSERT(charset() == to->charset());
DBUG_ASSERT(sort_charset() == to->sort_charset());
return Field::do_field_eq;
}
// ALTER from INET6 to another data type
if (to->charset() == &my_charset_bin &&
dynamic_cast<const Type_handler_general_purpose_string*>
(to->type_handler()))
{
/*
ALTER from INET6 to a binary string type, e.g.:
BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB
*/
return do_field_inet6_native_to_binary;
}
return do_field_string;
}
static void do_field_inet6_native_to_binary(Copy_field *copy)
{
NativeBufferInet6 res;
copy->from_field->val_native(&res);
copy->to_field->store(res.ptr(), res.length(), &my_charset_bin);
}
bool memcpy_field_possible(const Field *from) const override
{
// INSERT INTO t1 (inet6_field) SELECT field2 FROM t2;
return type_handler() == from->type_handler();
}
enum_conv_type rpl_conv_type_from(const Conv_source &source,
const Relay_log_info *rli,
const Conv_param &param) const override
{
if (type_handler() == source.type_handler() ||
(source.type_handler() == &type_handler_string &&
source.type_handler()->max_display_length_for_field(source) ==
Inet6::binary_length()))
return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
return CONV_TYPE_IMPOSSIBLE;
}
/*** Optimizer routines ***/
bool test_if_equality_guarantees_uniqueness(const Item *const_item) const override
{
/*
This condition:
WHERE inet6_field=const
should return a single distinct value only,
as comparison is done according to INET6.
*/
return true;
}
bool can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
override
{
switch (ctx.subst_constraint()) {
case ANY_SUBST:
return ctx.compare_type_handler() == item_equal->compare_type_handler();
case IDENTITY_SUBST:
return true;
}
return false;
}
Item *get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item) override;
bool can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const override
{
/*
Mixing of two different non-traditional types is currently prevented.
This may change in the future. For example, INET4 and INET6
data types can be made comparable.
But we allow mixing INET6 to a data type directly inherited from
a traditional type, e.g. INET6=VARCHAR/JSON.
*/
DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()->
is_traditional_scalar_type() ||
item->type_handler() == type_handler());
return true;
}
/**
Test if Field can use range optimizer for a standard comparison operation:
<=, <, =, <=>, >, >=
Note, this method does not cover spatial operations.
*/
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const override
{
// See the DBUG_ASSERT comment in can_optimize_keypart_ref()
DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()->
is_traditional_scalar_type() ||
item->type_handler() == type_handler());
return true;
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value) override
{
DBUG_ENTER("Field_inet6::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
DBUG_RETURN(NULL); /* Cannot infer anything */
}
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
bool can_optimize_hash_join(const Item_bool_func *cond,
const Item *item) const override
{
return can_optimize_keypart_ref(cond, item);
}
bool can_optimize_group_min_max(const Item_bool_func *cond,
const Item *const_item) const override
{
return true;
}
uint row_pack_length() const override { return pack_length(); }
Binlog_type_info binlog_type_info() const override
{
DBUG_ASSERT(type() == binlog_type());
return Binlog_type_info_fixed_string(Field_inet6::binlog_type(),
Inet6::binary_length(),
&my_charset_bin);
}
uchar *pack(uchar *to, const uchar *from, uint max_length) override
{
DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
return StringPack(&my_charset_bin, Inet6::binary_length()).
pack(to, from, max_length);
}
const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data) override
{
return StringPack(&my_charset_bin, Inet6::binary_length()).
unpack(to, from, from_end, param_data);
}
uint max_packed_col_length(uint max_length) override
{
return StringPack::max_packed_col_length(max_length);
}
uint packed_col_length(const uchar *data_ptr, uint length) override
{
return StringPack::packed_col_length(data_ptr, length);
}
/**********/
uint size_of() const override { return sizeof(*this); }
};
class Item_typecast_inet6: public Item_func
{
public:
Item_typecast_inet6(THD *thd, Item *a) :Item_func(thd, a) {}
const Type_handler *type_handler() const override
{ return &type_handler_inet6; }
enum Functype functype() const override { return CHAR_TYPECAST_FUNC; }
bool eq(const Item *item, bool binary_cmp) const override
{
if (this == item)
return true;
if (item->type() != FUNC_ITEM ||
functype() != ((Item_func*)item)->functype())
return false;
if (type_handler() != item->type_handler())
return false;
Item_typecast_inet6 *cast= (Item_typecast_inet6*) item;
return args[0]->eq(cast->args[0], binary_cmp);
}
LEX_CSTRING func_name_cstring() const override
{
static LEX_CSTRING name= {STRING_WITH_LEN("cast_as_inet6") };
return name;
}
void print(String *str, enum_query_type query_type) override
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as inet6)"));
}
bool fix_length_and_dec() override
{
Type_std_attributes::operator=(Type_std_attributes_inet6());
if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(args[0]))
set_maybe_null();
return false;
}
String *val_str(String *to) override
{
Inet6_null tmp(args[0]);
return (null_value= tmp.is_null() || tmp.to_string(to)) ? NULL : to;
}
longlong val_int() override
{
return 0;
}
double val_real() override
{
return 0;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to) override
{
Inet6_null tmp(args[0]);
return null_value= tmp.is_null() || tmp.to_native(to);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_typecast_inet6>(thd, this); }
};
class Item_cache_inet6: public Item_cache
{
NativeBufferInet6 m_value;
public:
Item_cache_inet6(THD *thd)
:Item_cache(thd, &type_handler_inet6)
{ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_cache_inet6>(thd, this); }
bool cache_value()
{
if (!example)
return false;
value_cached= true;
/*
Merge comments: in 10.7 this code migrated to
Item_cache_fbt in to sql/sql_type_fixedbin.h
*/
null_value_inside= null_value=
example->val_native_with_conversion_result(current_thd,
&m_value,
type_handler());
return true;
}
String* val_str(String *to)
{
if (!has_value())
return NULL;
Inet6_null tmp(m_value.ptr(), m_value.length());
return tmp.is_null() || tmp.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to)
{
if (!has_value())
return NULL;
my_decimal_set_zero(to);
return to;
}
longlong val_int()
{
if (!has_value())
return 0;
return 0;
}
double val_real()
{
if (!has_value())
return 0;
return 0;
}
longlong val_datetime_packed(THD *thd)
{
DBUG_ASSERT(0);
if (!has_value())
return 0;
return 0;
}
longlong val_time_packed(THD *thd)
{
DBUG_ASSERT(0);
if (!has_value())
return 0;
return 0;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (!has_value())
return true;
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to)
{
if (!has_value())
return true;
return to->copy(m_value.ptr(), m_value.length());
}
};
class Item_literal_inet6: public Item_literal
{
Inet6 m_value;
public:
Item_literal_inet6(THD *thd)
:Item_literal(thd),
m_value(Inet6_zero())
{ }
Item_literal_inet6(THD *thd, const Inet6 &value)
:Item_literal(thd),
m_value(value)
{ }
const Type_handler *type_handler() const override
{
return &type_handler_inet6;
}
longlong val_int() override
{
return 0;
}
double val_real() override
{
return 0;
}
String *val_str(String *to) override
{
return m_value.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to) override
{
return m_value.to_native(to);
}
void print(String *str, enum_query_type query_type) override
{
StringBufferInet6 tmp;
m_value.to_string(&tmp);
str->append(STRING_WITH_LEN("INET6'"));
str->append(tmp);
str->append('\'');
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_literal_inet6>(thd, this); }
// Non-overriding methods
void set_value(const Inet6 &value)
{
m_value= value;
}
};
class Item_copy_inet6: public Item_copy
{
NativeBufferInet6 m_value;
public:
Item_copy_inet6(THD *thd, Item *item_arg): Item_copy(thd, item_arg) {}
bool val_native(THD *thd, Native *to) override
{
if (null_value)
return true;
return to->copy(m_value.ptr(), m_value.length());
}
String *val_str(String *to) override
{
if (null_value)
return NULL;
Inet6_null tmp(m_value.ptr(), m_value.length());
return tmp.is_null() || tmp.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
double val_real() override
{
return 0;
}
longlong val_int() override
{
return 0;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return null_value;
}
void copy() override
{
null_value= item->val_native(current_thd, &m_value);
DBUG_ASSERT(null_value == item->null_value);
}
int save_in_field(Field *field, bool no_conversions) override
{
return Item::save_in_field(field, no_conversions);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_copy_inet6>(thd, this); }
};
class in_inet6 :public in_vector
{
Inet6 m_value;
static int cmp_inet6(void *cmp_arg, Inet6 *a, Inet6 *b)
{
return a->cmp(*b);
}
public:
in_inet6(THD *thd, uint elements)
:in_vector(thd, elements, sizeof(Inet6), (qsort2_cmp) cmp_inet6, 0),
m_value(Inet6_zero())
{ }
const Type_handler *type_handler() const override
{
return &type_handler_inet6;
}
void set(uint pos, Item *item) override
{
Inet6 *buff= &((Inet6 *) base)[pos];
Inet6_null value(item);
if (value.is_null())
*buff= Inet6_zero();
else
*buff= value;
}
uchar *get_value(Item *item) override
{
Inet6_null value(item);
if (value.is_null())
return 0;
m_value= value;
return (uchar *) &m_value;
}
Item* create_item(THD *thd) override
{
return new (thd->mem_root) Item_literal_inet6(thd);
}
void value_to_item(uint pos, Item *item) override
{
const Inet6 &buff= (((Inet6*) base)[pos]);
static_cast<Item_literal_inet6*>(item)->set_value(buff);
}
};
class Item_char_typecast_func_handler_inet6_to_binary:
public Item_handled_func::Handler_str
{
public:
const Type_handler *return_type_handler(const Item_handled_func *item)
const override
{
if (item->max_length > MAX_FIELD_VARCHARLENGTH)
return Type_handler::blob_type_handler(item->max_length);
if (item->max_length > 255)
return &type_handler_varchar;
return &type_handler_string;
}
bool fix_length_and_dec(Item_handled_func *xitem) const override
{
return false;
}
String *val_str(Item_handled_func *item, String *to) const override
{
DBUG_ASSERT(dynamic_cast<const Item_char_typecast*>(item));
return static_cast<Item_char_typecast*>(item)->
val_str_binary_from_native(to);
}
};
static Item_char_typecast_func_handler_inet6_to_binary
item_char_typecast_func_handler_inet6_to_binary;
bool Type_handler_inet6::
Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
{
if (item->cast_charset() == &my_charset_bin)
{
item->fix_length_and_dec_native_to_binary(Inet6::binary_length());
item->set_func_handler(&item_char_typecast_func_handler_inet6_to_binary);
return false;
}
item->fix_length_and_dec_str();
return false;
}
bool
Type_handler_inet6::character_or_binary_string_to_native(THD *thd,
const String *str,
Native *to) const
{
if (str->charset() == &my_charset_bin)
{
// Convert from a binary string
if (str->length() != Inet6::binary_length() ||
to->copy(str->ptr(), str->length()))
{
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(),
ErrConvString(str).ptr());
return true;
}
return false;
}
// Convert from a character string
Inet6_null tmp(*str);
if (tmp.is_null())
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(), ErrConvString(str).ptr());
return tmp.is_null() || tmp.to_native(to);
}
bool
Type_handler_inet6::Item_save_in_value(THD *thd, Item *item,
st_value *value) const
{
value->m_type= DYN_COL_STRING;
String *str= item->val_str(&value->m_string);
if (str != &value->m_string && !item->null_value)
{
// "item" returned a non-NULL value
if (Inet6_null(*str).is_null())
{
/*
The value was not-null, but conversion to INET6 failed:
SELECT a, DECODE_ORACLE(inet6col, 'garbage', '<NULL>', '::01', '01')
FROM t1;
*/
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(), ErrConvString(str).ptr());
value->m_type= DYN_COL_NULL;
return true;
}
// "item" returned a non-NULL value, and it was a valid INET6
value->m_string.set(str->ptr(), str->length(), str->charset());
}
return check_null(item, value);
}
void Type_handler_inet6::Item_param_setup_conversion(THD *thd,
Item_param *param) const
{
param->setup_conversion_string(thd, thd->variables.character_set_client);
}
void Type_handler_inet6::make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const
{
DBUG_ASSERT(item->type_handler() == this);
NativeBufferInet6 tmp;
item->val_native_result(current_thd, &tmp);
if (item->maybe_null())
{
if (item->null_value)
{
memset(to, 0, Inet6::binary_length() + 1);
return;
}
*to++= 1;
}
DBUG_ASSERT(!item->null_value);
DBUG_ASSERT(Inet6::binary_length() == tmp.length());
DBUG_ASSERT(Inet6::binary_length() == sort_field->length);
memcpy(to, tmp.ptr(), tmp.length());
}
uint
Type_handler_inet6::make_packed_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const
{
DBUG_ASSERT(item->type_handler() == this);
NativeBufferInet6 tmp;
item->val_native_result(current_thd, &tmp);
if (item->maybe_null())
{
if (item->null_value)
{
*to++=0;
return 0;
}
*to++= 1;
}
DBUG_ASSERT(!item->null_value);
DBUG_ASSERT(Inet6::binary_length() == tmp.length());
DBUG_ASSERT(Inet6::binary_length() == sort_field->length);
memcpy(to, tmp.ptr(), tmp.length());
return tmp.length();
}
void Type_handler_inet6::sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const
{
attr->original_length= attr->length= Inet6::binary_length();
attr->suffix_length= 0;
}
cmp_item *Type_handler_inet6::make_cmp_item(THD *thd, CHARSET_INFO *cs) const
{
return new (thd->mem_root) cmp_item_inet6;
}
in_vector *
Type_handler_inet6::make_in_vector(THD *thd, const Item_func_in *func,
uint nargs) const
{
return new (thd->mem_root) in_inet6(thd, nargs);
}
Item *Type_handler_inet6::create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr)
const
{
return new (thd->mem_root) Item_typecast_inet6(thd, item);
}
Item_cache *Type_handler_inet6::Item_get_cache(THD *thd, const Item *item) const
{
return new (thd->mem_root) Item_cache_inet6(thd);
}
Item_copy *Type_handler_inet6::create_item_copy(THD *thd, Item *item) const
{
return new (thd->mem_root) Item_copy_inet6(thd, item);
}
Item *
Type_handler_inet6::make_const_item_for_comparison(THD *thd,
Item *src,
const Item *cmp) const
{
Inet6_null tmp(src);
if (tmp.is_null())
return new (thd->mem_root) Item_null(thd, src->name.str);
return new (thd->mem_root) Item_literal_inet6(thd, tmp);
}
Item *Field_inet6::get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
Inet6_null tmp(const_item);
if (tmp.is_null())
return NULL;
return new (thd->mem_root) Item_literal_inet6(thd, tmp);
}
Field *
Type_handler_inet6::make_table_field_from_def(
TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const
{
return new (mem_root) Field_inet6(name, addr);
}
Field *Type_handler_inet6::make_table_field(MEM_ROOT *root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE_SHARE *share) const
{
return new (root) Field_inet6(name, addr);
}
Field *Type_handler_inet6::make_conversion_table_field(MEM_ROOT *root,
TABLE *table,
uint metadata,
const Field *target)
const
{
const Record_addr tmp(NULL, Bit_addr(true));
return new (table->in_use->mem_root) Field_inet6(&empty_clex_str, tmp);
}
bool Type_handler_inet6::partition_field_check(const LEX_CSTRING &field_name,
Item *item_expr) const
{
if (item_expr->cmp_type() != STRING_RESULT)
{
my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
return true;
}
return false;
}
bool
Type_handler_inet6::partition_field_append_value(
String *to,
Item *item_expr,
CHARSET_INFO *field_cs,
partition_value_print_mode_t mode)
const
{
StringBufferInet6 inet6str;
Inet6_null inet6(item_expr);
if (inet6.is_null())
{
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
return true;
}
return inet6.to_string(&inet6str) ||
to->append('\'') ||
to->append(inet6str) ||
to->append('\'');
}
/***************************************************************/
class Type_collection_inet: public Type_collection
{
const Type_handler *aggregate_common(const Type_handler *a,
const Type_handler *b) const
{
if (a == b)
return a;
return NULL;
}
const Type_handler *aggregate_if_string(const Type_handler *a,
const Type_handler *b) const
{
static const Type_aggregator::Pair agg[]=
{
{&type_handler_inet6, &type_handler_null, &type_handler_inet6},
{&type_handler_inet6, &type_handler_varchar, &type_handler_inet6},
{&type_handler_inet6, &type_handler_string, &type_handler_inet6},
{&type_handler_inet6, &type_handler_tiny_blob, &type_handler_inet6},
{&type_handler_inet6, &type_handler_blob, &type_handler_inet6},
{&type_handler_inet6, &type_handler_medium_blob, &type_handler_inet6},
{&type_handler_inet6, &type_handler_long_blob, &type_handler_inet6},
{&type_handler_inet6, &type_handler_hex_hybrid, &type_handler_inet6},
{NULL,NULL,NULL}
};
return Type_aggregator::find_handler_in_array(agg, a, b, true);
}
public:
const Type_handler *aggregate_for_result(const Type_handler *a,
const Type_handler *b)
const override
{
const Type_handler *h;
if ((h= aggregate_common(a, b)) ||
(h= aggregate_if_string(a, b)))
return h;
return NULL;
}
const Type_handler *aggregate_for_min_max(const Type_handler *a,
const Type_handler *b)
const override
{
return aggregate_for_result(a, b);
}
const Type_handler *aggregate_for_comparison(const Type_handler *a,
const Type_handler *b)
const override
{
if (const Type_handler *h= aggregate_common(a, b))
return h;
static const Type_aggregator::Pair agg[]=
{
{&type_handler_inet6, &type_handler_null, &type_handler_inet6},
{&type_handler_inet6, &type_handler_long_blob, &type_handler_inet6},
{NULL,NULL,NULL}
};
return Type_aggregator::find_handler_in_array(agg, a, b, true);
}
const Type_handler *aggregate_for_num_op(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
{
if (type_handler_inet6.name().eq(name))
return &type_handler_inet6;
return NULL;
}
};
const Type_collection *Type_handler_inet6::type_collection() const
const Name &Inet6::default_value()
{
static Type_collection_inet type_collection_inet;
return &type_collection_inet;
static Name def(STRING_WITH_LEN("::"));
return def;
}
#ifndef SQL_TYPE_INET_H
#define SQL_TYPE_INET_H
/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
/* Copyright (c) 2011,2013, Oracle and/or its affiliates.
Copyright (c) 2014 MariaDB Foundation
Copyright (c) 2019 MariaDB Corporation
Copyright (c) 2019,2021 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -31,14 +31,20 @@ static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
*/
static const uint IN6_ADDR_MAX_CHAR_LENGTH= 8 * 4 + 7;
#include "sql_type_fixedbin_storage.h"
class NativeBufferInet6: public NativeBuffer<IN6_ADDR_SIZE+1>
class Inet6: public FixedBinTypeStorage<IN6_ADDR_SIZE, IN6_ADDR_MAX_CHAR_LENGTH>
{
public:
using FixedBinTypeStorage::FixedBinTypeStorage;
bool ascii_to_fbt(const char *str, size_t str_length);
size_t to_string(char *dst, size_t dstsize) const;
static const Name &default_value();
};
class StringBufferInet6: public StringBuffer<IN6_ADDR_MAX_CHAR_LENGTH+1>
{
};
#include "sql_type_fixedbin.h"
typedef FixedBinTypeBundle<Inet6> Inet6Bundle;
/***********************************************************************/
......@@ -132,898 +138,4 @@ class Inet4_null: public Inet4, public Null_flag
}
};
class Inet6
{
protected:
char m_buffer[IN6_ADDR_SIZE];
bool make_from_item(Item *item, bool warn);
bool ascii_to_ipv6(const char *str, size_t str_length);
bool character_string_to_ipv6(const char *str, size_t str_length,
CHARSET_INFO *cs)
{
if (cs->state & MY_CS_NONASCII)
{
char tmp[IN6_ADDR_MAX_CHAR_LENGTH];
String_copier copier;
uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
cs, str, str_length);
return ascii_to_ipv6(tmp, length);
}
return ascii_to_ipv6(str, str_length);
}
bool make_from_character_or_binary_string(const String *str, bool warn);
bool binary_to_ipv6(const char *str, size_t length)
{
if (length != sizeof(m_buffer))
return true;
memcpy(m_buffer, str, length);
return false;
}
Inet6() { }
public:
static uint binary_length() { return IN6_ADDR_SIZE; }
/**
Non-abbreviated syntax is 8 groups, up to 4 digits each,
plus 7 delimiters between the groups.
Abbreviated syntax is even shorter.
*/
static uint max_char_length() { return IN6_ADDR_MAX_CHAR_LENGTH; }
static bool only_zero_bytes(const char *ptr, uint length)
{
for (uint i= 0 ; i < length; i++)
{
if (ptr[i] != 0)
return false;
}
return true;
}
/*
Check at Item's fix_fields() time if "item" can return a nullable value
on conversion to INET6, or conversion produces a NOT NULL INET6 value.
*/
static bool fix_fields_maybe_null_on_conversion_to_inet6(Item *item);
public:
Inet6(Item *item, bool *error, bool warn= true)
{
*error= make_from_item(item, warn);
}
void to_binary(char *str, size_t str_size) const
{
DBUG_ASSERT(str_size >= sizeof(m_buffer));
memcpy(str, m_buffer, sizeof(m_buffer));
}
bool to_binary(String *to) const
{
return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
}
bool to_native(Native *to) const
{
return to->copy(m_buffer, sizeof(m_buffer));
}
size_t to_string(char *dst, size_t dstsize) const;
bool to_string(String *to) const
{
to->set_charset(&my_charset_latin1);
if (to->alloc(INET6_ADDRSTRLEN))
return true;
to->length((uint32) to_string((char*) to->ptr(), INET6_ADDRSTRLEN));
return false;
}
bool is_v4compat() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer);
}
bool is_v4mapped() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer);
}
int cmp(const char *str, size_t length) const
{
DBUG_ASSERT(length == sizeof(m_buffer));
return memcmp(m_buffer, str, length);
}
int cmp(const Binary_string &other) const
{
return cmp(other.ptr(), other.length());
}
int cmp(const Inet6 &other) const
{
return memcmp(m_buffer, other.m_buffer, sizeof(m_buffer));
}
};
class Inet6_zero: public Inet6
{
public:
Inet6_zero()
{
bzero(&m_buffer, sizeof(m_buffer));
}
};
class Inet6_null: public Inet6, public Null_flag
{
public:
// Initialize from a text representation
Inet6_null(const char *str, size_t length, CHARSET_INFO *cs)
:Null_flag(character_string_to_ipv6(str, length, cs))
{ }
Inet6_null(const String &str)
:Inet6_null(str.ptr(), str.length(), str.charset())
{ }
// Initialize from a binary representation
Inet6_null(const char *str, size_t length)
:Null_flag(binary_to_ipv6(str, length))
{ }
Inet6_null(const Binary_string &str)
:Inet6_null(str.ptr(), str.length())
{ }
// Initialize from an Item
Inet6_null(Item *item, bool warn= true)
:Null_flag(make_from_item(item, warn))
{ }
public:
const Inet6& to_inet6() const
{
DBUG_ASSERT(!is_null());
return *this;
}
void to_binary(char *str, size_t str_size) const
{
to_inet6().to_binary(str, str_size);
}
bool to_binary(String *to) const
{
return to_inet6().to_binary(to);
}
size_t to_string(char *dst, size_t dstsize) const
{
return to_inet6().to_string(dst, dstsize);
}
bool to_string(String *to) const
{
return to_inet6().to_string(to);
}
bool is_v4compat() const
{
return to_inet6().is_v4compat();
}
bool is_v4mapped() const
{
return to_inet6().is_v4mapped();
}
};
class Type_std_attributes_inet6: public Type_std_attributes
{
public:
Type_std_attributes_inet6()
:Type_std_attributes(
Type_numeric_attributes(Inet6::max_char_length(), 0, true),
DTCollation_numeric())
{ }
};
class Type_handler_inet6: public Type_handler
{
bool character_or_binary_string_to_native(THD *thd, const String *str,
Native *to) const;
public:
~Type_handler_inet6() override {}
const Type_collection *type_collection() const override;
const Name &default_value() const override
{
static Name def(STRING_WITH_LEN("::"));
return def;
}
protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_STRING;
}
bool Item_append_extended_type_info(Send_field_extended_metadata *to,
const Item *item) const override
{
return to->set_data_type_name(name().lex_cstring());
}
enum_field_types field_type() const override
{
return MYSQL_TYPE_STRING;
}
Item_result result_type() const override
{
return STRING_RESULT;
}
Item_result cmp_type() const override
{
return STRING_RESULT;
}
enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
const override
{
return DYN_COL_STRING;
}
uint32 max_display_length_for_field(const Conv_source &src) const override
{
return Inet6::max_char_length();
}
const Type_handler *type_handler_for_comparison() const override
{
return this;
}
int
stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override
{
DBUG_ASSERT(field->type_handler() == this);
Inet6_null ni(item); // Convert Item to INET6
if (ni.is_null())
return 0;
NativeBufferInet6 tmp;
if (field->val_native(&tmp))
{
DBUG_ASSERT(0);
return 0;
}
return -ni.cmp(tmp);
}
CHARSET_INFO *charset_for_protocol(const Item *item) const override
{
return item->collation.collation;
}
bool is_scalar_type() const override { return true; }
bool is_val_native_ready() const override { return true; }
bool can_return_int() const override { return false; }
bool can_return_decimal() const override { return false; }
bool can_return_real() const override { return false; }
bool can_return_str() const override { return true; }
bool can_return_text() const override { return true; }
bool can_return_date() const override { return false; }
bool can_return_time() const override { return false; }
bool convert_to_binary_using_val_native() const override { return true; }
decimal_digits_t Item_time_precision(THD *thd, Item *item) const override
{
return 0;
}
decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override
{
return 0;
}
decimal_digits_t Item_decimal_scale(const Item *item) const override
{
return 0;
}
decimal_digits_t Item_decimal_precision(const Item *item) const override
{
/*
This will be needed if we ever allow cast from INET6 to DECIMAL.
Decimal precision of INET6 is 39 digits:
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' =
340282366920938463463374607431768211456 = 39 digits
*/
return 39;
}
/*
Returns how many digits a divisor adds into a division result.
See Item::divisor_precision_increment() in item.h for more comments.
*/
decimal_digits_t Item_divisor_precision_increment(const Item *) const override
{
return 0;
}
/**
Makes a temporary table Field to handle numeric aggregate functions,
e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc.
*/
Field *make_num_distinct_aggregator_field(MEM_ROOT *,
const Item *) const override
{
DBUG_ASSERT(0);
return 0;
}
Field *make_conversion_table_field(MEM_ROOT *root,
TABLE *TABLE,
uint metadata,
const Field *target) const override;
// Fix attributes after the parser
bool Column_definition_fix_attributes(Column_definition *c) const override
{
c->length= Inet6::max_char_length();
return false;
}
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *def,
handler *file,
ulonglong table_flags,
const Column_derived_attributes
*derived_attr)
const override
{
def->prepare_stage1_simple(&my_charset_numeric);
return false;
}
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file)
const override
{
def->redefine_stage1_common(dup, file);
def->set_compression_method(dup->compression_method());
def->create_length_to_internal_length_string();
return false;
}
bool Column_definition_prepare_stage2(Column_definition *def,
handler *file,
ulonglong table_flags) const override
{
def->pack_flag= FIELDFLAG_BINARY;
return false;
}
bool partition_field_check(const LEX_CSTRING &field_name,
Item *item_expr) const override;
bool partition_field_append_value(String *to,
Item *item_expr,
CHARSET_INFO *field_cs,
partition_value_print_mode_t mode)
const override;
Field *make_table_field(MEM_ROOT *root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE_SHARE *table) const override;
Field *
make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const override;
void
Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
uchar *buff) const override
{
def->frm_pack_basic(buff);
def->frm_pack_charset(buff);
}
bool
Column_definition_attributes_frm_unpack(Column_definition_attributes *def,
TABLE_SHARE *share,
const uchar *buffer,
LEX_CUSTRING *gis_options)
const override
{
def->frm_unpack_basic(buffer);
return def->frm_unpack_charset(share, buffer);
}
void make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param)
const override;
uint make_packed_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const override;
void sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const override;
uint32 max_display_length(const Item *item) const override
{
return Inet6::max_char_length();
}
uint32 calc_pack_length(uint32 length) const override
{
return Inet6::binary_length();
}
void Item_update_null_value(Item *item) const override
{
NativeBufferInet6 tmp;
item->val_native(current_thd, &tmp);
}
bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
void Item_param_setup_conversion(THD *thd, Item_param *param) const override;
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const override
{
param->set_param_str(pos, len);
}
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
const st_value *val) const override
{
param->unsigned_flag= false;//QQ
param->setup_conversion_string(thd, attr->collation.collation);
/*
Exact value of max_length is not known unless data is converted to
charset of connection, so we have to set it later.
*/
return param->set_str(val->m_string.ptr(), val->m_string.length(),
attr->collation.collation,
attr->collation.collation);
}
bool Item_param_val_native(THD *thd, Item_param *item, Native *to)
const override
{
StringBufferInet6 buffer;
String *str= item->val_str(&buffer);
if (!str)
return true;
Inet6_null tmp(*str);
return tmp.is_null() || tmp.to_native(to);
}
bool Item_send(Item *item, Protocol *p, st_value *buf) const override
{
return Item_send_str(item, p, buf);
}
int Item_save_in_field(Item *item, Field *field, bool no_conversions)
const override
{
if (field->type_handler() == this)
{
NativeBuffer<MAX_FIELD_WIDTH> tmp;
bool rc= item->val_native(current_thd, &tmp);
if (rc || item->null_value)
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_native(tmp);
}
return item->save_str_in_field(field, no_conversions);
}
String *print_item_value(THD *thd, Item *item, String *str) const override
{
StringBufferInet6 buf;
String *result= item->val_str(&buf);
/*
TODO: This should eventually use one of these notations:
1. CAST('::' AS INET6)
Problem: CAST is not supported as a NAME_CONST() argument.
2. INET6'::
Problem: This syntax is not supported by the parser yet.
*/
return !result ||
str->realloc(result->length() + 2) ||
str->append(STRING_WITH_LEN("'")) ||
str->append(result->ptr(), result->length()) ||
str->append(STRING_WITH_LEN("'")) ?
NULL :
str;
}
/**
Check if
WHERE expr=value AND expr=const
can be rewritten as:
WHERE const=value AND expr=const
"this" is the comparison handler that is used by "target".
@param target - the predicate expr=value,
whose "expr" argument will be replaced to "const".
@param target_expr - the target's "expr" which will be replaced to "const".
@param target_value - the target's second argument, it will remain unchanged.
@param source - the equality predicate expr=const (or expr<=>const)
that can be used to rewrite the "target" part
(under certain conditions, see the code).
@param source_expr - the source's "expr". It should be exactly equal to
the target's "expr" to make condition rewrite possible.
@param source_const - the source's "const" argument, it will be inserted
into "target" instead of "expr".
*/
bool
can_change_cond_ref_to_const(Item_bool_func2 *target,
Item *target_expr, Item *target_value,
Item_bool_func2 *source,
Item *source_expr, Item *source_const)
const override
{
/*
WHERE COALESCE(inet6_col)='::1' AND COALESCE(inet6_col)=CONCAT(a); -->
WHERE COALESCE(inet6_col)='::1' AND '::1'=CONCAT(a);
*/
return target->compare_type_handler() == source->compare_type_handler();
}
bool
subquery_type_allows_materialization(const Item *inner,
const Item *outer, bool) const override
{
/*
Example:
SELECT * FROM t1 WHERE a IN (SELECT inet6col FROM t1 GROUP BY inet6col);
Allow materialization only if the outer column is also INET6.
This can be changed for more relaxed rules in the future.
*/
DBUG_ASSERT(inner->type_handler() == this);
return outer->type_handler() == this;
}
/**
Make a simple constant replacement item for a constant "src",
so the new item can futher be used for comparison with "cmp", e.g.:
src = cmp -> replacement = cmp
"this" is the type handler that is used to compare "src" and "cmp".
@param thd - current thread, for mem_root
@param src - The item that we want to replace. It's a const item,
but it can be complex enough to calculate on every row.
@param cmp - The src's comparand.
@retval - a pointer to the created replacement Item
@retval - NULL, if could not create a replacement (e.g. on EOM).
NULL is also returned for ROWs, because instead of replacing
a Item_row to a new Item_row, Type_handler_row just replaces
its elements.
*/
Item *make_const_item_for_comparison(THD *thd,
Item *src,
const Item *cmp) const override;
Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const override;
Item_copy *create_item_copy(THD *thd, Item *item) const override;
int cmp_native(const Native &a, const Native &b) const override
{
DBUG_ASSERT(a.length() == Inet6::binary_length());
DBUG_ASSERT(b.length() == Inet6::binary_length());
return memcmp(a.ptr(), b.ptr(), Inet6::binary_length());
}
bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override
{
return cmp->set_cmp_func_native(thd);
}
bool Item_const_eq(const Item_const *a, const Item_const *b,
bool binary_cmp) const override
{
return false;//QQ
}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const override
{
Inet6_null na(a);
Inet6_null nb(b);
return !na.is_null() && !nb.is_null() && !na.cmp(nb);
}
bool Item_hybrid_func_fix_attributes(THD *thd,
const LEX_CSTRING &name,
Type_handler_hybrid_field_type *h,
Type_all_attributes *attr,
Item **items,
uint nitems) const override
{
attr->Type_std_attributes::operator=(Type_std_attributes_inet6());
h->set_handler(this);
/*
If some of the arguments cannot be safely converted to "INET6 NOT NULL",
then mark the entire function nullability as NULL-able.
Otherwise, keep the generic nullability calculated by earlier stages:
- either by the most generic way in Item_func::fix_fields()
- or by Item_func_xxx::fix_length_and_dec() before the call of
Item_hybrid_func_fix_attributes()
IFNULL() is special. It does not need to test args[0].
*/
uint first= dynamic_cast<Item_func_ifnull*>(attr) ? 1 : 0;
for (uint i= first; i < nitems; i++)
{
if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(items[i]))
{
attr->set_type_maybe_null(true);
break;
}
}
return false;
}
bool Item_func_min_max_fix_attributes(THD *thd,
Item_func_min_max *func,
Item **items,
uint nitems) const override
{
return Item_hybrid_func_fix_attributes(thd, func->func_name_cstring(),
func, func, items, nitems);
}
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override
{
func->Type_std_attributes::operator=(Type_std_attributes_inet6());
func->set_handler(this);
return false;
}
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_val_native_with_conversion(THD *thd, Item *item,
Native *to) const override
{
if (item->type_handler() == this)
return item->val_native(thd, to); // No conversion needed
StringBufferInet6 buffer;
String *str= item->val_str(&buffer);
return str ? character_or_binary_string_to_native(thd, str, to) : true;
}
bool Item_val_native_with_conversion_result(THD *thd, Item *item,
Native *to) const override
{
if (item->type_handler() == this)
return item->val_native_result(thd, to); // No conversion needed
StringBufferInet6 buffer;
String *str= item->str_result(&buffer);
return str ? character_or_binary_string_to_native(thd, str, to) : true;
}
bool Item_val_bool(Item *item) const override
{
NativeBufferInet6 tmp;
if (item->val_native(current_thd, &tmp))
return false;
return !Inet6::only_zero_bytes(tmp.ptr(), tmp.length());
}
void Item_get_date(THD *thd, Item *item,
Temporal::Warn *buff, MYSQL_TIME *ltime,
date_mode_t fuzzydate) const override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
}
longlong Item_val_int_signed_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
longlong Item_val_int_unsigned_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str)
const override
{
NativeBufferInet6 tmp;
if ((item->null_value= item->arguments()[0]->val_native(current_thd, &tmp)))
return NULL;
DBUG_ASSERT(tmp.length() == Inet6::binary_length());
if (str->set_hex(tmp.ptr(), tmp.length()))
{
str->length(0);
str->set_charset(item->collation.collation);
}
return str;
}
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *item,
String *str) const override
{
NativeBufferInet6 native;
if (item->val_native(current_thd, &native))
{
DBUG_ASSERT(item->null_value);
return NULL;
}
DBUG_ASSERT(native.length() == Inet6::binary_length());
Inet6_null tmp(native.ptr(), native.length());
return tmp.is_null() || tmp.to_string(str) ? NULL : str;
}
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
const override
{
return 0;
}
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
const override
{
return 0;
}
my_decimal *
Item_func_hybrid_field_type_val_decimal(Item_func_hybrid_field_type *,
my_decimal *to) const override
{
my_decimal_set_zero(to);
return to;
}
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *to,
date_mode_t fuzzydate)
const override
{
set_zero_time(to, MYSQL_TIMESTAMP_TIME);
}
// WHERE is Item_func_min_max_val_native???
String *Item_func_min_max_val_str(Item_func_min_max *func, String *str)
const override
{
Inet6_null tmp(func);
return tmp.is_null() || tmp.to_string(str) ? NULL : str;
}
double Item_func_min_max_val_real(Item_func_min_max *) const override
{
return 0;
}
longlong Item_func_min_max_val_int(Item_func_min_max *) const override
{
return 0;
}
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *to) const override
{
my_decimal_set_zero(to);
return to;
}
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
MYSQL_TIME *to, date_mode_t fuzzydate)
const override
{
set_zero_time(to, MYSQL_TIMESTAMP_TIME);
return false;
}
bool
Item_func_between_fix_length_and_dec(Item_func_between *func) const override
{
return false;
}
longlong Item_func_between_val_int(Item_func_between *func) const override
{
return func->val_int_cmp_native();
}
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
in_vector *make_in_vector(THD *thd, const Item_func_in *func,
uint nargs) const override;
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *func)
const override
{
if (func->compatible_types_scalar_bisection_possible())
{
return func->value_list_convert_const_to_int(thd) ||
func->fix_for_scalar_comparison_using_bisection(thd);
}
return
func->fix_for_scalar_comparison_using_cmp_items(thd,
1U << (uint) STRING_RESULT);
}
bool
Item_func_round_fix_length_and_dec(Item_func_round *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool
Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_abs_fix_length_and_dec(Item_func_abs *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_neg_fix_length_and_dec(Item_func_neg *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool
Item_func_signed_fix_length_and_dec(Item_func_signed *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_double_typecast_fix_length_and_dec(Item_double_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_float_typecast_fix_length_and_dec(Item_float_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_char_typecast_fix_length_and_dec(Item_char_typecast *item)
const override;
bool
Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_plus_fix_length_and_dec(Item_func_plus *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_minus_fix_length_and_dec(Item_func_minus *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_mul_fix_length_and_dec(Item_func_mul *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_div_fix_length_and_dec(Item_func_div *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool
Item_func_mod_fix_length_and_dec(Item_func_mod *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
};
extern MYSQL_PLUGIN_IMPORT Type_handler_inet6 type_handler_inet6;
#endif /* SQL_TYPE_INET_H */
......@@ -484,6 +484,11 @@ class Binary_string: public Sql_alloc
if (str.Alloced_length)
Alloced_length= (uint32) (str.Alloced_length - offset);
}
LEX_CSTRING to_lex_cstring() const
{
LEX_CSTRING tmp= {Ptr, str_length};
return tmp;
}
inline LEX_CSTRING *get_value(LEX_CSTRING *res)
{
res->str= Ptr;
......
#ifndef SQL_TYPE_FIXEDBIN_H
#define SQL_TYPE_FIXEDBIN_H
/* Copyright (c) 2019,2021 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/*
This is a common code for plugin (?) types that are generally
handled like strings, but have their own fixed size on-disk binary storage
format and their own (variable size) canonical string representation.
Examples are INET6 and UUID types.
*/
#define MYSQL_SERVER
#include "sql_class.h" // THD, SORT_FIELD_ATTR
#include "opt_range.h" // SEL_ARG, null_element
#include "sql_type_fixedbin_storage.h"
/***********************************************************************/
template<class FbtImpl>
class FixedBinTypeBundle
{
public:
class Fbt: public FbtImpl
{
protected:
using FbtImpl::m_buffer;
bool make_from_item(Item *item, bool warn)
{
if (item->type_handler() == type_handler_fbt())
{
Native tmp(m_buffer, sizeof(m_buffer));
bool rc= item->val_native(current_thd, &tmp);
if (rc)
return true;
DBUG_ASSERT(tmp.length() == sizeof(m_buffer));
if (tmp.ptr() != m_buffer)
memcpy(m_buffer, tmp.ptr(), sizeof(m_buffer));
return false;
}
StringBuffer<FbtImpl::max_char_length()+1> tmp;
String *str= item->val_str(&tmp);
return str ? make_from_character_or_binary_string(str, warn) : true;
}
bool character_string_to_fbt(const char *str, size_t str_length,
CHARSET_INFO *cs)
{
if (cs->state & MY_CS_NONASCII)
{
char tmp[FbtImpl::max_char_length()+1];
String_copier copier;
uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
cs, str, str_length);
return FbtImpl::ascii_to_fbt(tmp, length);
}
return FbtImpl::ascii_to_fbt(str, str_length);
}
bool make_from_character_or_binary_string(const String *str, bool warn)
{
if (str->charset() != &my_charset_bin)
{
bool rc= character_string_to_fbt(str->ptr(), str->length(),
str->charset());
if (rc && warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
type_handler_fbt()->name().ptr(), ErrConvString(str).ptr());
return rc;
}
if (str->length() != sizeof(m_buffer))
{
if (warn)
current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
type_handler_fbt()->name().ptr(), ErrConvString(str).ptr());
return true;
}
DBUG_ASSERT(str->ptr() != m_buffer);
memcpy(m_buffer, str->ptr(), sizeof(m_buffer));
return false;
}
bool binary_to_fbt(const char *str, size_t length)
{
if (length != sizeof(m_buffer))
return true;
memcpy(m_buffer, str, length);
return false;
}
Fbt() { }
public:
static Fbt zero()
{
Fbt fbt;
fbt.set_zero();
return fbt;
}
static Fbt record_to_memory(const char *ptr)
{
Fbt fbt;
FbtImpl::record_to_memory(fbt.m_buffer, ptr);
return fbt;
}
/*
Check at Item's fix_fields() time if "item" can return a nullable value
on conversion to Fbt, or conversion produces a NOT NULL Fbt value.
*/
static bool fix_fields_maybe_null_on_conversion_to_fbt(Item *item)
{
if (item->maybe_null())
return true;
if (item->type_handler() == type_handler_fbt())
return false;
if (!item->const_item() || item->is_expensive())
return true;
return Fbt_null(item, false).is_null();
}
public:
Fbt(Item *item, bool *error, bool warn= true)
{
*error= make_from_item(item, warn);
}
void to_record(char *str, size_t str_size) const
{
DBUG_ASSERT(str_size >= sizeof(m_buffer));
FbtImpl::memory_to_record(str, m_buffer);
}
bool to_binary(String *to) const
{
return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
}
bool to_native(Native *to) const
{
return to->copy(m_buffer, sizeof(m_buffer));
}
bool to_string(String *to) const
{
to->set_charset(&my_charset_latin1);
if (to->alloc(FbtImpl::max_char_length()+1))
return true;
to->length((uint32) FbtImpl::to_string(const_cast<char*>(to->ptr()),
FbtImpl::max_char_length()+1));
return false;
}
int cmp(const Binary_string &other) const
{
return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring());
}
int cmp(const Fbt &other) const
{
return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring());
}
};
class Fbt_null: public Fbt, public Null_flag
{
public:
// Initialize from a text representation
Fbt_null(const char *str, size_t length, CHARSET_INFO *cs)
:Null_flag(Fbt::character_string_to_fbt(str, length, cs)) { }
Fbt_null(const String &str)
:Fbt_null(str.ptr(), str.length(), str.charset()) { }
// Initialize from a binary representation
Fbt_null(const char *str, size_t length)
:Null_flag(Fbt::binary_to_fbt(str, length)) { }
Fbt_null(const Binary_string &str)
:Fbt_null(str.ptr(), str.length()) { }
// Initialize from an Item
Fbt_null(Item *item, bool warn= true)
:Null_flag(Fbt::make_from_item(item, warn)) { }
public:
const Fbt& to_fbt() const
{
DBUG_ASSERT(!is_null());
return *this;
}
void to_record(char *str, size_t str_size) const
{
to_fbt().to_record(str, str_size);
}
bool to_binary(String *to) const
{
return to_fbt().to_binary(to);
}
size_t to_string(char *dst, size_t dstsize) const
{
return to_fbt().to_string(dst, dstsize);
}
bool to_string(String *to) const
{
return to_fbt().to_string(to);
}
};
class Type_std_attributes_fbt: public Type_std_attributes
{
public:
Type_std_attributes_fbt()
:Type_std_attributes(
Type_numeric_attributes(FbtImpl::max_char_length(), 0, true),
DTCollation_numeric())
{ }
};
class Type_handler_fbt: public Type_handler
{
bool character_or_binary_string_to_native(THD *thd, const String *str,
Native *to) const
{
if (str->charset() == &my_charset_bin)
{
// Convert from a binary string
if (str->length() != FbtImpl::binary_length() ||
to->copy(str->ptr(), str->length()))
{
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(), ErrConvString(str).ptr());
return true;
}
return false;
}
// Convert from a character string
Fbt_null tmp(*str);
if (tmp.is_null())
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(), ErrConvString(str).ptr());
return tmp.is_null() || tmp.to_native(to);
}
public:
~Type_handler_fbt() override {}
const Type_collection *type_collection() const override
{
static Type_collection_fbt type_collection_fbt;
return &type_collection_fbt;
}
const Name &default_value() const override
{
return FbtImpl::default_value();
}
ulong KEY_pack_flags(uint column_nr) const override
{
return FbtImpl::KEY_pack_flags(column_nr);
}
protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_STRING;
}
bool Item_append_extended_type_info(Send_field_extended_metadata *to,
const Item *item) const override
{
return to->set_data_type_name(name().lex_cstring());
}
enum_field_types field_type() const override
{
return MYSQL_TYPE_STRING;
}
Item_result result_type() const override
{
return STRING_RESULT;
}
Item_result cmp_type() const override
{
return STRING_RESULT;
}
enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
const override
{
return DYN_COL_STRING;
}
uint32 max_display_length_for_field(const Conv_source &src) const override
{
return FbtImpl::max_char_length();
}
const Type_handler *type_handler_for_comparison() const override
{
return this;
}
int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override
{
DBUG_ASSERT(field->type_handler() == this);
Fbt_null ni(item); // Convert Item to Fbt
if (ni.is_null())
return 0;
NativeBuffer<FbtImpl::binary_length()+1> tmp;
if (field->val_native(&tmp))
{
DBUG_ASSERT(0);
return 0;
}
return -ni.cmp(tmp);
}
CHARSET_INFO *charset_for_protocol(const Item *item) const override
{
return item->collation.collation;
}
bool is_scalar_type() const override { return true; }
bool is_val_native_ready() const override { return true; }
bool can_return_int() const override { return false; }
bool can_return_decimal() const override { return false; }
bool can_return_real() const override { return false; }
bool can_return_str() const override { return true; }
bool can_return_text() const override { return true; }
bool can_return_date() const override { return false; }
bool can_return_time() const override { return false; }
bool convert_to_binary_using_val_native() const override { return true; }
decimal_digits_t Item_time_precision(THD *thd, Item *item) const override
{
return 0;
}
decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override
{
return 0;
}
decimal_digits_t Item_decimal_scale(const Item *item) const override
{
return 0;
}
decimal_digits_t Item_decimal_precision(const Item *item) const override
{
/* This will be needed if we ever allow cast from Fbt to DECIMAL. */
return (FbtImpl::binary_length()*8+7)/10*3; // = bytes to decimal digits
}
/*
Returns how many digits a divisor adds into a division result.
See Item::divisor_precision_increment() in item.h for more comments.
*/
decimal_digits_t Item_divisor_precision_increment(const Item *) const override
{
return 0;
}
/**
Makes a temporary table Field to handle numeric aggregate functions,
e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc.
*/
Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override
{
DBUG_ASSERT(0);
return 0;
}
Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata,
const Field *target) const override
{
const Record_addr tmp(NULL, Bit_addr(true));
return new (table->in_use->mem_root) Field_fbt(&empty_clex_str, tmp);
}
// Fix attributes after the parser
bool Column_definition_fix_attributes(Column_definition *c) const override
{
c->length= FbtImpl::max_char_length();
return false;
}
bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root,
Column_definition *def, handler *file,
ulonglong table_flags,
const Column_derived_attributes *derived_attr)
const override
{
def->prepare_stage1_simple(&my_charset_numeric);
return false;
}
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file) const override
{
def->redefine_stage1_common(dup, file);
def->set_compression_method(dup->compression_method());
def->create_length_to_internal_length_string();
return false;
}
bool Column_definition_prepare_stage2(Column_definition *def, handler *file,
ulonglong table_flags) const override
{
def->pack_flag= FIELDFLAG_BINARY;
return false;
}
bool partition_field_check(const LEX_CSTRING &field_name,
Item *item_expr) const override
{
if (item_expr->cmp_type() != STRING_RESULT)
{
my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
return true;
}
return false;
}
bool partition_field_append_value(String *to, Item *item_expr,
CHARSET_INFO *field_cs,
partition_value_print_mode_t mode)
const override
{
StringBuffer<FbtImpl::max_char_length()+64> fbtstr;
Fbt_null fbt(item_expr);
if (fbt.is_null())
{
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
return true;
}
return fbt.to_string(&fbtstr) ||
to->append('\'') ||
to->append(fbtstr) ||
to->append('\'');
}
Field *make_table_field(MEM_ROOT *root, const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE_SHARE *table) const override
{
return new (root) Field_fbt(name, addr);
}
Field * make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
const LEX_CSTRING *name, const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
uint32 flags) const override
{
return new (mem_root) Field_fbt(name, addr);
}
void Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
uchar *buff) const override
{
def->frm_pack_basic(buff);
def->frm_pack_charset(buff);
}
bool Column_definition_attributes_frm_unpack(Column_definition_attributes *def,
TABLE_SHARE *share, const uchar *buffer,
LEX_CUSTRING *gis_options)
const override
{
def->frm_unpack_basic(buffer);
return def->frm_unpack_charset(share, buffer);
}
void make_sort_key_part(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const override
{
DBUG_ASSERT(item->type_handler() == this);
NativeBuffer<FbtImpl::binary_length()+1> tmp;
item->val_native_result(current_thd, &tmp);
if (item->maybe_null())
{
if (item->null_value)
{
memset(to, 0, FbtImpl::binary_length() + 1);
return;
}
*to++= 1;
}
DBUG_ASSERT(!item->null_value);
DBUG_ASSERT(FbtImpl::binary_length() == tmp.length());
DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length);
FbtImpl::memory_to_record((char*) to, tmp.ptr());
}
uint make_packed_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const override
{
DBUG_ASSERT(item->type_handler() == this);
NativeBuffer<FbtImpl::binary_length()+1> tmp;
item->val_native_result(current_thd, &tmp);
if (item->maybe_null())
{
if (item->null_value)
{
*to++=0;
return 0;
}
*to++= 1;
}
DBUG_ASSERT(!item->null_value);
DBUG_ASSERT(FbtImpl::binary_length() == tmp.length());
DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length);
FbtImpl::memory_to_record((char*) to, tmp.ptr());
return tmp.length();
}
void sort_length(THD *thd, const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const override
{
attr->original_length= attr->length= FbtImpl::binary_length();
attr->suffix_length= 0;
}
uint32 max_display_length(const Item *item) const override
{
return FbtImpl::max_char_length();
}
uint32 calc_pack_length(uint32 length) const override
{
return FbtImpl::binary_length();
}
void Item_update_null_value(Item *item) const override
{
NativeBuffer<FbtImpl::binary_length()+1> tmp;
item->val_native(current_thd, &tmp);
}
bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override
{
value->m_type= DYN_COL_STRING;
String *str= item->val_str(&value->m_string);
if (str != &value->m_string && !item->null_value)
{
// "item" returned a non-NULL value
if (Fbt_null(*str).is_null())
{
/*
The value was not-null, but conversion to FBT failed:
SELECT a, DECODE_ORACLE(fbtcol, 'garbage', '<NULL>', '::01', '01')
FROM t1;
*/
thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN,
name().ptr(), ErrConvString(str).ptr());
value->m_type= DYN_COL_NULL;
return true;
}
// "item" returned a non-NULL value, and it was a valid FBT
value->m_string.set(str->ptr(), str->length(), str->charset());
}
return check_null(item, value);
}
void Item_param_setup_conversion(THD *thd, Item_param *param) const override
{
param->setup_conversion_string(thd, thd->variables.character_set_client);
}
void Item_param_set_param_func(Item_param *param,
uchar **pos, ulong len) const override
{
param->set_param_str(pos, len);
}
bool Item_param_set_from_value(THD *thd, Item_param *param,
const Type_all_attributes *attr,
const st_value *val) const override
{
param->unsigned_flag= false;
param->setup_conversion_string(thd, attr->collation.collation);
/*
Exact value of max_length is not known unless fbt is converted to
charset of connection, so we have to set it later.
*/
return param->set_str(val->m_string.ptr(), val->m_string.length(),
attr->collation.collation,
attr->collation.collation);
}
bool Item_param_val_native(THD *thd, Item_param *item, Native *to)
const override
{
StringBuffer<FbtImpl::max_char_length()+1> buffer;
String *str= item->val_str(&buffer);
if (!str)
return true;
Fbt_null tmp(*str);
return tmp.is_null() || tmp.to_native(to);
}
bool Item_send(Item *item, Protocol *p, st_value *buf) const override
{
return Item_send_str(item, p, buf);
}
int Item_save_in_field(Item *item, Field *field, bool no_conversions)
const override
{
if (field->type_handler() == this)
{
NativeBuffer<MAX_FIELD_WIDTH> tmp;
bool rc= item->val_native(current_thd, &tmp);
if (rc || item->null_value)
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
return field->store_native(tmp);
}
return item->save_str_in_field(field, no_conversions);
}
String *print_item_value(THD *thd, Item *item, String *str) const override
{
StringBuffer<FbtImpl::max_char_length()+64> buf;
String *result= item->val_str(&buf);
/*
TODO: This should eventually use one of these notations:
1. CAST('xxx' AS Fbt)
Problem: CAST is not supported as a NAME_CONST() argument.
2. Fbt'xxx'
Problem: This syntax is not supported by the parser yet.
*/
return !result || str->realloc(result->length() + 2) ||
str->append(STRING_WITH_LEN("'")) ||
str->append(result->ptr(), result->length()) ||
str->append(STRING_WITH_LEN("'")) ? nullptr : str;
}
/**
Check if
WHERE expr=value AND expr=const
can be rewritten as:
WHERE const=value AND expr=const
"this" is the comparison handler that is used by "target".
@param target - the predicate expr=value,
whose "expr" argument will be replaced to "const".
@param target_expr - the target's "expr" which will be replaced to "const".
@param target_value - the target's second argument, it will remain unchanged.
@param source - the equality predicate expr=const (or expr<=>const)
that can be used to rewrite the "target" part
(under certain conditions, see the code).
@param source_expr - the source's "expr". It should be exactly equal to
the target's "expr" to make condition rewrite possible.
@param source_const - the source's "const" argument, it will be inserted
into "target" instead of "expr".
*/
bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr,
Item *target_value, Item_bool_func2 *source,
Item *source_expr, Item *source_const)
const override
{
/*
WHERE COALESCE(col)='xxx' AND COALESCE(col)=CONCAT(a); -->
WHERE COALESCE(col)='xxx' AND 'xxx'=CONCAT(a);
*/
return target->compare_type_handler() == source->compare_type_handler();
}
bool subquery_type_allows_materialization(const Item *inner,
const Item *outer, bool) const override
{
/*
Example:
SELECT * FROM t1 WHERE a IN (SELECT col FROM t1 GROUP BY col);
Allow materialization only if the outer column is also FBT.
This can be changed for more relaxed rules in the future.
*/
DBUG_ASSERT(inner->type_handler() == this);
return outer->type_handler() == this;
}
/**
Make a simple constant replacement item for a constant "src",
so the new item can futher be used for comparison with "cmp", e.g.:
src = cmp -> replacement = cmp
"this" is the type handler that is used to compare "src" and "cmp".
@param thd - current thread, for mem_root
@param src - The item that we want to replace. It's a const item,
but it can be complex enough to calculate on every row.
@param cmp - The src's comparand.
@retval - a pointer to the created replacement Item
@retval - NULL, if could not create a replacement (e.g. on EOM).
NULL is also returned for ROWs, because instead of replacing
a Item_row to a new Item_row, Type_handler_row just replaces
its elements.
*/
Item *make_const_item_for_comparison(THD *thd, Item *src,
const Item *cmp) const override
{
Fbt_null tmp(src);
if (tmp.is_null())
return new (thd->mem_root) Item_null(thd, src->name.str);
return new (thd->mem_root) Item_literal_fbt(thd, tmp);
}
Item_cache *Item_get_cache(THD *thd, const Item *item) const override
{
return new (thd->mem_root) Item_cache_fbt(thd);
}
Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const override
{
return new (thd->mem_root) Item_typecast_fbt(thd, item);
}
Item_copy *create_item_copy(THD *thd, Item *item) const override
{
return new (thd->mem_root) Item_copy_fbt(thd, item);
}
int cmp_native(const Native &a, const Native &b) const override
{
return FbtImpl::cmp(a.to_lex_cstring(), b.to_lex_cstring());
}
bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override
{
return cmp->set_cmp_func_native(thd);
}
bool Item_const_eq(const Item_const *a, const Item_const *b,
bool binary_cmp) const override
{
return false;
}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
Item *a, Item *b) const override
{
Fbt_null na(a), nb(b);
return !na.is_null() && !nb.is_null() && !na.cmp(nb);
}
bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name,
Type_handler_hybrid_field_type *h,
Type_all_attributes *attr,
Item **items, uint nitems) const override
{
attr->Type_std_attributes::operator=(Type_std_attributes_fbt());
h->set_handler(this);
/*
If some of the arguments cannot be safely converted to "FBT NOT NULL",
then mark the entire function nullability as NULL-able.
Otherwise, keep the generic nullability calculated by earlier stages:
- either by the most generic way in Item_func::fix_fields()
- or by Item_func_xxx::fix_length_and_dec() before the call of
Item_hybrid_func_fix_attributes()
IFNULL() is special. It does not need to test args[0].
*/
uint first= dynamic_cast<Item_func_ifnull*>(attr) ? 1 : 0;
for (uint i= first; i < nitems; i++)
{
if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(items[i]))
{
attr->set_type_maybe_null(true);
break;
}
}
return false;
}
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
Item **items, uint nitems) const override
{
return Item_hybrid_func_fix_attributes(thd, func->func_name_cstring(),
func, func, items, nitems);
}
bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override
{
func->Type_std_attributes::operator=(Type_std_attributes_fbt());
func->set_handler(this);
return false;
}
bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_val_native_with_conversion(THD *thd, Item *item,
Native *to) const override
{
if (item->type_handler() == this)
return item->val_native(thd, to); // No conversion needed
StringBuffer<FbtImpl::max_char_length()+1> buffer;
String *str= item->val_str(&buffer);
return str ? character_or_binary_string_to_native(thd, str, to) : true;
}
bool Item_val_native_with_conversion_result(THD *thd, Item *item,
Native *to) const override
{
if (item->type_handler() == this)
return item->val_native_result(thd, to); // No conversion needed
StringBuffer<FbtImpl::max_char_length()+1> buffer;
String *str= item->str_result(&buffer);
return str ? character_or_binary_string_to_native(thd, str, to) : true;
}
bool Item_val_bool(Item *item) const override
{
NativeBuffer<FbtImpl::binary_length()+1> tmp;
if (item->val_native(current_thd, &tmp))
return false;
return !Fbt::only_zero_bytes(tmp.ptr(), tmp.length());
}
void Item_get_date(THD *thd, Item *item, Temporal::Warn *buff,
MYSQL_TIME *ltime, date_mode_t fuzzydate) const override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
}
longlong Item_val_int_signed_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
longlong Item_val_int_unsigned_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str)
const override
{
NativeBuffer<FbtImpl::binary_length()+1> tmp;
if ((item->null_value= item->arguments()[0]->val_native(current_thd, &tmp)))
return nullptr;
DBUG_ASSERT(tmp.length() == FbtImpl::binary_length());
if (str->set_hex(tmp.ptr(), tmp.length()))
{
str->length(0);
str->set_charset(item->collation.collation);
}
return str;
}
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *item,
String *str) const override
{
NativeBuffer<FbtImpl::binary_length()+1> native;
if (item->val_native(current_thd, &native))
{
DBUG_ASSERT(item->null_value);
return nullptr;
}
DBUG_ASSERT(native.length() == FbtImpl::binary_length());
Fbt_null tmp(native.ptr(), native.length());
return tmp.is_null() || tmp.to_string(str) ? nullptr : str;
}
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
const override
{
return 0;
}
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
const override
{
return 0;
}
my_decimal *
Item_func_hybrid_field_type_val_decimal(Item_func_hybrid_field_type *,
my_decimal *to) const override
{
my_decimal_set_zero(to);
return to;
}
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *to,
date_mode_t fuzzydate)
const override
{
set_zero_time(to, MYSQL_TIMESTAMP_TIME);
}
// WHERE is Item_func_min_max_val_native???
String *Item_func_min_max_val_str(Item_func_min_max *func, String *str)
const override
{
Fbt_null tmp(func);
return tmp.is_null() || tmp.to_string(str) ? nullptr : str;
}
double Item_func_min_max_val_real(Item_func_min_max *) const override
{
return 0;
}
longlong Item_func_min_max_val_int(Item_func_min_max *) const override
{
return 0;
}
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
my_decimal *to) const override
{
my_decimal_set_zero(to);
return to;
}
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, MYSQL_TIME *to,
date_mode_t fuzzydate) const override
{
set_zero_time(to, MYSQL_TIMESTAMP_TIME);
return false;
}
bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override
{
return false;
}
longlong Item_func_between_val_int(Item_func_between *func) const override
{
return func->val_int_cmp_native();
}
cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override
{
return new (thd->mem_root) cmp_item_fbt;
}
in_vector *make_in_vector(THD *thd, const Item_func_in *func,
uint nargs) const override
{
return new (thd->mem_root) in_fbt(thd, nargs);
}
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
Item_func_in *func)
const override
{
if (func->compatible_types_scalar_bisection_possible())
{
return func->value_list_convert_const_to_int(thd) ||
func->fix_for_scalar_comparison_using_bisection(thd);
}
return
func->fix_for_scalar_comparison_using_cmp_items(thd,
1U << (uint) STRING_RESULT);
}
bool Item_func_round_fix_length_and_dec(Item_func_round *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_abs_fix_length_and_dec(Item_func_abs *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_neg_fix_length_and_dec(Item_func_neg *func) const override
{
return Item_func_or_sum_illegal_param(func);
}
bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *item)
const override
{
if (item->cast_charset() == &my_charset_bin)
{
static Item_char_typecast_func_handler_fbt_to_binary
item_char_typecast_func_handler_fbt_to_binary;
item->fix_length_and_dec_native_to_binary(FbtImpl::binary_length());
item->set_func_handler(&item_char_typecast_func_handler_fbt_to_binary);
return false;
}
item->fix_length_and_dec_str();
return false;
}
bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item)
const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_plus_fix_length_and_dec(Item_func_plus *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_minus_fix_length_and_dec(Item_func_minus *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_mul_fix_length_and_dec(Item_func_mul *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_div_fix_length_and_dec(Item_func_div *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
bool Item_func_mod_fix_length_and_dec(Item_func_mod *item) const override
{
return Item_func_or_sum_illegal_param(item);
}
};
class cmp_item_fbt: public cmp_item_scalar
{
Fbt m_native;
public:
cmp_item_fbt()
:cmp_item_scalar(),
m_native(Fbt::zero())
{ }
void store_value(Item *item) override
{
m_native= Fbt(item, &m_null_value);
}
int cmp_not_null(const Value *val) override
{
DBUG_ASSERT(!val->is_null());
DBUG_ASSERT(val->is_string());
Fbt_null tmp(val->m_string);
DBUG_ASSERT(!tmp.is_null());
return m_native.cmp(tmp);
}
int cmp(Item *arg) override
{
Fbt_null tmp(arg);
return m_null_value || tmp.is_null() ? UNKNOWN : m_native.cmp(tmp) != 0;
}
int compare(cmp_item *ci) override
{
cmp_item_fbt *tmp= static_cast<cmp_item_fbt*>(ci);
DBUG_ASSERT(!m_null_value);
DBUG_ASSERT(!tmp->m_null_value);
return m_native.cmp(tmp->m_native);
}
cmp_item *make_same(THD *thd) override
{
return new (thd->mem_root) cmp_item_fbt();
}
};
class Field_fbt: public Field
{
static void set_min_value(char *ptr)
{
memset(ptr, 0, FbtImpl::binary_length());
}
static void set_max_value(char *ptr)
{
memset(ptr, 0xFF, FbtImpl::binary_length());
}
void store_warning(const ErrConv &str,
Sql_condition::enum_warning_level level)
{
if (get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION)
return;
const TABLE_SHARE *s= table->s;
static const Name type_name= type_handler_fbt()->name();
get_thd()->push_warning_truncated_value_for_field(level, type_name.ptr(),
str.ptr(), s ? s->db.str : nullptr, s ? s->table_name.str : nullptr,
field_name.str);
}
int set_null_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_null();
return 1;
}
int set_min_value_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_min_value((char*) ptr);
return 1;
}
int set_max_value_with_warn(const ErrConv &str)
{
store_warning(str, Sql_condition::WARN_LEVEL_WARN);
set_max_value((char*) ptr);
return 1;
}
int store_fbt_null_with_warn(const Fbt_null &fbt,
const ErrConvString &err)
{
DBUG_ASSERT(marked_for_write_or_computed());
if (fbt.is_null())
return maybe_null() ? set_null_with_warn(err)
: set_min_value_with_warn(err);
fbt.to_record((char *) ptr, FbtImpl::binary_length());
return 0;
}
public:
Field_fbt(const LEX_CSTRING *field_name_arg, const Record_addr &rec)
:Field(rec.ptr(), FbtImpl::max_char_length(),
rec.null_ptr(), rec.null_bit(), Field::NONE, field_name_arg)
{
flags|= BINARY_FLAG | UNSIGNED_FLAG;
}
const Type_handler *type_handler() const override
{
return type_handler_fbt();
}
uint32 max_display_length() const override { return field_length; }
bool str_needs_quotes() const override { return true; }
const DTCollation &dtcollation() const override
{
static DTCollation_numeric c;
return c;
}
CHARSET_INFO *charset(void) const override { return &my_charset_numeric; }
const CHARSET_INFO *sort_charset(void) const override { return &my_charset_bin; }
/**
This makes client-server protocol convert the value according
to @@character_set_client.
*/
bool binary() const override { return false; }
enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
bool is_equal(const Column_definition &new_field) const override
{
return new_field.type_handler() == type_handler();
}
bool eq_def(const Field *field) const override
{
return Field::eq_def(field);
}
double pos_in_interval(Field *min, Field *max) override
{
return pos_in_interval_val_str(min, max, 0);
}
int cmp(const uchar *a, const uchar *b) const override
{ return memcmp(a, b, pack_length()); }
void sort_string(uchar *to, uint length) override
{
DBUG_ASSERT(length == pack_length());
memcpy(to, ptr, length);
}
uint32 pack_length() const override
{
return FbtImpl::binary_length();
}
uint pack_length_from_metadata(uint field_metadata) const override
{
return FbtImpl::binary_length();
}
void sql_type(String &str) const override
{
static Name name= type_handler_fbt()->name();
str.set_ascii(name.ptr(), name.length());
}
void make_send_field(Send_field *to) override
{
Field::make_send_field(to);
to->set_data_type_name(type_handler_fbt()->name().lex_cstring());
}
bool validate_value_in_record(THD *thd, const uchar *record) const override
{
return false;
}
bool val_native(Native *to) override
{
DBUG_ASSERT(marked_for_read());
if (to->alloc(FbtImpl::binary_length()))
return true;
to->length(FbtImpl::binary_length());
FbtImpl::record_to_memory((char*) to->ptr(), (const char*) ptr);
return false;
}
Fbt to_fbt() const
{
DBUG_ASSERT(marked_for_read());
return Fbt::record_to_memory((const char*) ptr);
}
String *val_str(String *val_buffer, String *) override
{
return to_fbt().to_string(val_buffer) ? NULL : val_buffer;
}
my_decimal *val_decimal(my_decimal *to) override
{
DBUG_ASSERT(marked_for_read());
my_decimal_set_zero(to);
return to;
}
longlong val_int() override
{
DBUG_ASSERT(marked_for_read());
return 0;
}
double val_real() override
{
DBUG_ASSERT(marked_for_read());
return 0;
}
bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
DBUG_ASSERT(marked_for_read());
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_bool(void) override
{
DBUG_ASSERT(marked_for_read());
return !Fbt::only_zero_bytes((const char *) ptr, FbtImpl::binary_length());
}
int store_native(const Native &value) override
{
DBUG_ASSERT(marked_for_write_or_computed());
DBUG_ASSERT(value.length() == FbtImpl::binary_length());
FbtImpl::memory_to_record((char*) ptr, value.ptr());
return 0;
}
int store(const char *str, size_t length, CHARSET_INFO *cs) override
{
return cs == &my_charset_bin ? store_binary(str, length)
: store_text(str, length, cs);
}
int store_text(const char *str, size_t length, CHARSET_INFO *cs) override
{
return store_fbt_null_with_warn(Fbt_null(str, length, cs),
ErrConvString(str, length, cs));
}
int store_binary(const char *str, size_t length) override
{
return store_fbt_null_with_warn(Fbt_null(str, length),
ErrConvString(str, length,
&my_charset_bin));
}
int store_hex_hybrid(const char *str, size_t length) override
{
return Field_fbt::store_binary(str, length);
}
int store_decimal(const my_decimal *num) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvDecimal(num));
}
int store(longlong nr, bool unsigned_flag) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(
ErrConvInteger(Longlong_hybrid(nr, unsigned_flag)));
}
int store(double nr) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvDouble(nr));
}
int store_time_dec(const MYSQL_TIME *ltime, uint dec) override
{
DBUG_ASSERT(marked_for_write_or_computed());
return set_min_value_with_warn(ErrConvTime(ltime));
}
/*** Field conversion routines ***/
int store_field(Field *from) override
{
// INSERT INTO t1 (fbt_field) SELECT different_field_type FROM t2;
return from->save_in_field(this);
}
int save_in_field(Field *to) override
{
// INSERT INTO t2 (different_field_type) SELECT fbt_field FROM t1;
if (to->charset() == &my_charset_bin &&
dynamic_cast<const Type_handler_general_purpose_string*>
(to->type_handler()))
{
NativeBuffer<FbtImpl::binary_length()+1> res;
val_native(&res);
return to->store(res.ptr(), res.length(), &my_charset_bin);
}
return save_in_field_str(to);
}
Copy_func *get_copy_func(const Field *from) const override
{
// ALTER to FBT from another field
return do_field_string;
}
Copy_func *get_copy_func_to(const Field *to) const override
{
if (type_handler() == to->type_handler())
{
// ALTER from FBT to FBT
DBUG_ASSERT(pack_length() == to->pack_length());
DBUG_ASSERT(charset() == to->charset());
DBUG_ASSERT(sort_charset() == to->sort_charset());
return Field::do_field_eq;
}
// ALTER from FBT to another fbt type
if (to->charset() == &my_charset_bin &&
dynamic_cast<const Type_handler_general_purpose_string*>
(to->type_handler()))
{
/*
ALTER from FBT to a binary string type, e.g.:
BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB
*/
return do_field_fbt_native_to_binary;
}
return do_field_string;
}
static void do_field_fbt_native_to_binary(Copy_field *copy)
{
NativeBuffer<FbtImpl::binary_length()+1> res;
copy->from_field->val_native(&res);
copy->to_field->store(res.ptr(), res.length(), &my_charset_bin);
}
bool memcpy_field_possible(const Field *from) const override
{
// INSERT INTO t1 (fbt_field) SELECT field2 FROM t2;
return type_handler() == from->type_handler();
}
enum_conv_type rpl_conv_type_from(const Conv_source &source,
const Relay_log_info *rli,
const Conv_param &param) const override
{
if (type_handler() == source.type_handler() ||
(source.type_handler() == &type_handler_string &&
source.type_handler()->max_display_length_for_field(source) ==
FbtImpl::binary_length()))
return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
return CONV_TYPE_IMPOSSIBLE;
}
/*** Optimizer routines ***/
bool test_if_equality_guarantees_uniqueness(const Item *const_item) const override
{
/*
This condition:
WHERE fbt_field=const
should return a single distinct value only,
as comparison is done according to FBT.
*/
return true;
}
bool can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
override
{
switch (ctx.subst_constraint()) {
case ANY_SUBST:
return ctx.compare_type_handler() == item_equal->compare_type_handler();
case IDENTITY_SUBST:
return true;
}
return false;
}
Item *get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item) override
{
Fbt_null tmp(const_item);
if (tmp.is_null())
return NULL;
return new (thd->mem_root) Item_literal_fbt(thd, tmp);
}
bool can_optimize_keypart_ref(const Item_bool_func *cond,
const Item *item) const override
{
/*
Mixing of two different non-traditional types is currently prevented.
This may change in the future.
*/
DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()->
is_traditional_scalar_type() ||
item->type_handler() == type_handler());
return true;
}
/**
Test if Field can use range optimizer for a standard comparison operation:
<=, <, =, <=>, >, >=
Note, this method does not cover spatial operations.
*/
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
bool is_eq_func) const override
{
// See the DBUG_ASSERT comment in can_optimize_keypart_ref()
DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()->
is_traditional_scalar_type() ||
item->type_handler() == type_handler());
return true;
}
void hash(ulong *nr, ulong *nr2) override
{
if (is_null())
*nr^= (*nr << 1) | 1;
else
FbtImpl::hash_record(ptr, nr, nr2);
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part,
const Item_bool_func *cond,
scalar_comparison_op op, Item *value) override
{
DBUG_ENTER("Field_fbt::get_mm_leaf");
if (!can_optimize_scalar_range(prm, key_part, cond, op, value))
DBUG_RETURN(0);
int err= value->save_in_field_no_warnings(this, 1);
if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0)
DBUG_RETURN(&null_element);
if (err > 0)
{
if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL)
DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this));
DBUG_RETURN(NULL); /* Cannot infer anything */
}
DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value));
}
bool can_optimize_hash_join(const Item_bool_func *cond,
const Item *item) const override
{
return can_optimize_keypart_ref(cond, item);
}
bool can_optimize_group_min_max(const Item_bool_func *cond,
const Item *const_item) const override
{
return true;
}
uint row_pack_length() const override { return pack_length(); }
Binlog_type_info binlog_type_info() const override
{
DBUG_ASSERT(type() == binlog_type());
return Binlog_type_info_fixed_string(Field_fbt::binlog_type(),
FbtImpl::binary_length(), &my_charset_bin);
}
uchar *pack(uchar *to, const uchar *from, uint max_length) override
{
DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
return FbtImpl::pack(to, from, max_length);
}
const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data) override
{
return FbtImpl::unpack(to, from, from_end, param_data);
}
uint max_packed_col_length(uint max_length) override
{
return StringPack::max_packed_col_length(max_length);
}
uint packed_col_length(const uchar *fbt_ptr, uint length) override
{
return StringPack::packed_col_length(fbt_ptr, length);
}
uint size_of() const override { return sizeof(*this); }
};
class Item_typecast_fbt: public Item_func
{
public:
Item_typecast_fbt(THD *thd, Item *a) :Item_func(thd, a) {}
const Type_handler *type_handler() const override
{ return type_handler_fbt(); }
enum Functype functype() const override { return CHAR_TYPECAST_FUNC; }
bool eq(const Item *item, bool binary_cmp) const override
{
if (this == item)
return true;
if (item->type() != FUNC_ITEM ||
functype() != ((Item_func*)item)->functype())
return false;
if (type_handler() != item->type_handler())
return false;
Item_typecast_fbt *cast= (Item_typecast_fbt*) item;
return args[0]->eq(cast->args[0], binary_cmp);
}
LEX_CSTRING func_name_cstring() const override
{
static Name name= type_handler_fbt()->name();
size_t len= 9+name.length()+1;
char *buf= (char*)current_thd->alloc(len);
strmov(strmov(buf, "cast_as_"), name.ptr());
return { buf, len };
}
void print(String *str, enum_query_type query_type) override
{
str->append(STRING_WITH_LEN("cast("));
args[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" as "));
str->append(type_handler_fbt()->name().lex_cstring());
str->append(')');
}
bool fix_length_and_dec() override
{
Type_std_attributes::operator=(Type_std_attributes_fbt());
if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(args[0]))
set_maybe_null();
return false;
}
String *val_str(String *to) override
{
Fbt_null tmp(args[0]);
return (null_value= tmp.is_null() || tmp.to_string(to)) ? NULL : to;
}
longlong val_int() override
{
return 0;
}
double val_real() override
{
return 0;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to) override
{
Fbt_null tmp(args[0]);
return null_value= tmp.is_null() || tmp.to_native(to);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_typecast_fbt>(thd, this); }
};
class Item_cache_fbt: public Item_cache
{
NativeBuffer<FbtImpl::binary_length()+1> m_value;
public:
Item_cache_fbt(THD *thd)
:Item_cache(thd, type_handler_fbt()) { }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_cache_fbt>(thd, this); }
bool cache_value()
{
if (!example)
return false;
value_cached= true;
null_value_inside= null_value=
example->val_native_with_conversion_result(current_thd,
&m_value, type_handler());
return true;
}
String* val_str(String *to)
{
if (!has_value())
return NULL;
Fbt_null tmp(m_value.ptr(), m_value.length());
return tmp.is_null() || tmp.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to)
{
if (!has_value())
return NULL;
my_decimal_set_zero(to);
return to;
}
longlong val_int()
{
if (!has_value())
return 0;
return 0;
}
double val_real()
{
if (!has_value())
return 0;
return 0;
}
longlong val_datetime_packed(THD *thd)
{
DBUG_ASSERT(0);
if (!has_value())
return 0;
return 0;
}
longlong val_time_packed(THD *thd)
{
DBUG_ASSERT(0);
if (!has_value())
return 0;
return 0;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (!has_value())
return true;
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to)
{
if (!has_value())
return true;
return to->copy(m_value.ptr(), m_value.length());
}
};
class Item_literal_fbt: public Item_literal
{
Fbt m_value;
public:
Item_literal_fbt(THD *thd)
:Item_literal(thd),
m_value(Fbt::zero())
{ }
Item_literal_fbt(THD *thd, const Fbt &value)
:Item_literal(thd),
m_value(value)
{ }
const Type_handler *type_handler() const override
{
return type_handler_fbt();
}
longlong val_int() override
{
return 0;
}
double val_real() override
{
return 0;
}
String *val_str(String *to) override
{
return m_value.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return false;
}
bool val_native(THD *thd, Native *to) override
{
return m_value.to_native(to);
}
void print(String *str, enum_query_type query_type) override
{
StringBuffer<FbtImpl::max_char_length()+64> tmp;
tmp.append(type_handler_fbt()->name().lex_cstring());
my_caseup_str(&my_charset_latin1, tmp.c_ptr());
str->append(tmp);
str->append('\'');
m_value.to_string(&tmp);
str->append(tmp);
str->append('\'');
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_literal_fbt>(thd, this); }
// Non-overriding methods
void set_value(const Fbt &value)
{
m_value= value;
}
};
class Item_copy_fbt: public Item_copy
{
NativeBuffer<Fbt::binary_length()+1> m_value;
public:
Item_copy_fbt(THD *thd, Item *item_arg): Item_copy(thd, item_arg) {}
bool val_native(THD *thd, Native *to) override
{
if (null_value)
return true;
return to->copy(m_value.ptr(), m_value.length());
}
String *val_str(String *to) override
{
if (null_value)
return NULL;
Fbt_null tmp(m_value.ptr(), m_value.length());
return tmp.is_null() || tmp.to_string(to) ? NULL : to;
}
my_decimal *val_decimal(my_decimal *to) override
{
my_decimal_set_zero(to);
return to;
}
double val_real() override
{
return 0;
}
longlong val_int() override
{
return 0;
}
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
return null_value;
}
void copy() override
{
null_value= item->val_native(current_thd, &m_value);
DBUG_ASSERT(null_value == item->null_value);
}
int save_in_field(Field *field, bool no_conversions) override
{
return Item::save_in_field(field, no_conversions);
}
Item *get_copy(THD *thd) override
{ return get_item_copy<Item_copy_fbt>(thd, this); }
};
class in_fbt :public in_vector
{
Fbt m_value;
static int cmp_fbt(void *cmp_arg, Fbt *a, Fbt *b)
{
return a->cmp(*b);
}
public:
in_fbt(THD *thd, uint elements)
:in_vector(thd, elements, sizeof(Fbt), (qsort2_cmp) cmp_fbt, 0),
m_value(Fbt::zero())
{ }
const Type_handler *type_handler() const override
{
return type_handler_fbt();
}
void set(uint pos, Item *item) override
{
Fbt *buff= &((Fbt *) base)[pos];
Fbt_null value(item);
if (value.is_null())
*buff= Fbt::zero();
else
*buff= value;
}
uchar *get_value(Item *item) override
{
Fbt_null value(item);
if (value.is_null())
return 0;
m_value= value;
return (uchar *) &m_value;
}
Item* create_item(THD *thd) override
{
return new (thd->mem_root) Item_literal_fbt(thd);
}
void value_to_item(uint pos, Item *item) override
{
const Fbt &buff= (((Fbt*) base)[pos]);
static_cast<Item_literal_fbt*>(item)->set_value(buff);
}
};
class Item_char_typecast_func_handler_fbt_to_binary:
public Item_handled_func::Handler_str
{
public:
const Type_handler *return_type_handler(const Item_handled_func *item)
const override
{
if (item->max_length > MAX_FIELD_VARCHARLENGTH)
return Type_handler::blob_type_handler(item->max_length);
if (item->max_length > 255)
return &type_handler_varchar;
return &type_handler_string;
}
bool fix_length_and_dec(Item_handled_func *xitem) const override
{
return false;
}
String *val_str(Item_handled_func *item, String *to) const override
{
DBUG_ASSERT(dynamic_cast<const Item_char_typecast*>(item));
return static_cast<Item_char_typecast*>(item)->
val_str_binary_from_native(to);
}
};
class Type_collection_fbt: public Type_collection
{
const Type_handler *aggregate_common(const Type_handler *a,
const Type_handler *b) const
{
if (a == b)
return a;
return NULL;
}
const Type_handler *aggregate_if_string(const Type_handler *a,
const Type_handler *b) const
{
static const Type_aggregator::Pair agg[]=
{
{type_handler_fbt(), &type_handler_null, type_handler_fbt()},
{type_handler_fbt(), &type_handler_varchar, type_handler_fbt()},
{type_handler_fbt(), &type_handler_string, type_handler_fbt()},
{type_handler_fbt(), &type_handler_tiny_blob, type_handler_fbt()},
{type_handler_fbt(), &type_handler_blob, type_handler_fbt()},
{type_handler_fbt(), &type_handler_medium_blob, type_handler_fbt()},
{type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()},
{type_handler_fbt(), &type_handler_hex_hybrid, type_handler_fbt()},
{NULL,NULL,NULL}
};
return Type_aggregator::find_handler_in_array(agg, a, b, true);
}
public:
const Type_handler *aggregate_for_result(const Type_handler *a,
const Type_handler *b)
const override
{
const Type_handler *h;
if ((h= aggregate_common(a, b)) ||
(h= aggregate_if_string(a, b)))
return h;
return NULL;
}
const Type_handler *aggregate_for_min_max(const Type_handler *a,
const Type_handler *b)
const override
{
return aggregate_for_result(a, b);
}
const Type_handler *aggregate_for_comparison(const Type_handler *a,
const Type_handler *b)
const override
{
if (const Type_handler *h= aggregate_common(a, b))
return h;
static const Type_aggregator::Pair agg[]=
{
{type_handler_fbt(), &type_handler_null, type_handler_fbt()},
{type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()},
{NULL,NULL,NULL}
};
return Type_aggregator::find_handler_in_array(agg, a, b, true);
}
const Type_handler *aggregate_for_num_op(const Type_handler *a,
const Type_handler *b)
const override
{
return NULL;
}
const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
{
if (type_handler_fbt()->name().eq(name))
return type_handler_fbt();
return NULL;
}
};
static Type_handler_fbt *type_handler_fbt()
{
static Type_handler_fbt th;
return &th;
}
};
#endif /* SQL_TYPE_FIXEDBIN_H */
#ifndef SQL_TYPE_FIXEDBIN_STORAGE
#define SQL_TYPE_FIXEDBIN_STORAGE
/* Copyright (c) 2019,2021 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/*
This is a common code for plugin (?) types that are generally
handled like strings, but have their own fixed size on-disk binary storage
format and their own (variable size) canonical string representation.
Examples are INET6 and UUID types.
The MariaDB server uses three binary representations of a data type:
1. In-memory binary representation (user visible)
This representation:
- can be used in INSERT..VALUES (X'AABBCC')
- can be used in WHERE conditions: WHERE c1=X'AABBCC'
- is returned by CAST(x AS BINARY(N))
- is returned by Field::val_native() and Item::val_native()
2. In-record binary representation (user invisible)
This representation:
- is used in records (is pointed by Field::ptr)
- must be comparable by memcmp()
3. Binlog binary (row) representation
Usually, for string data types the binlog representation
is based on the in-record representation with trailing byte compression:
- trailing space compression for text string data types
- trailing zero compression for binary string data types
We have to have separate in-memory and in-record representations
because we use HA_KEYTYPE_BINARY for indexing. The engine API
does not have a way to pass a comparison function as a parameter.
The default implementation below assumes that:
- the in-memory and in-record representations are equal
- the binlog representation is compatible with BINARY(N)
This is OK for simple data types, like INET6.
Data type implementations that need different representations
can override the default implementation (like e.g. UUID does).
*/
/***********************************************************************/
template<size_t NATIVE_LEN, size_t MAX_CHAR_LEN>
class FixedBinTypeStorage
{
protected:
// The buffer that stores the in-memory binary representation
char m_buffer[NATIVE_LEN];
// Non-initializing constructor
FixedBinTypeStorage()
{ }
FixedBinTypeStorage & set_zero()
{
bzero(&m_buffer, sizeof(m_buffer));
return *this;
}
public:
// Initialize from the in-memory binary representation
FixedBinTypeStorage(const char *str, size_t length)
{
if (length != binary_length())
set_zero();
else
memcpy(&m_buffer, str, sizeof(m_buffer));
}
// Return the buffer with the in-memory representation
Lex_cstring to_lex_cstring() const
{
return Lex_cstring(m_buffer, sizeof(m_buffer));
}
static constexpr uint binary_length() { return NATIVE_LEN; }
static constexpr uint max_char_length() { return MAX_CHAR_LEN; }
// Compare the in-memory binary representations of two values
static int cmp(const LEX_CSTRING &a, const LEX_CSTRING &b)
{
DBUG_ASSERT(a.length == binary_length());
DBUG_ASSERT(b.length == binary_length());
return memcmp(a.str, b.str, b.length);
}
/*
Convert from the in-memory to the in-record representation.
Used in Field::store_native().
*/
static void memory_to_record(char *to, const char *from)
{
memcpy(to, from, NATIVE_LEN);
}
/*
Convert from the in-record to the in-memory representation
Used in Field::val_native().
*/
static void record_to_memory(char *to, const char *from)
{
memcpy(to, from, NATIVE_LEN);
}
/*
Hash the in-record representation
Used in Field::hash().
*/
static void hash_record(const uchar *ptr, ulong *nr, ulong *nr2)
{
my_charset_bin.hash_sort(ptr, binary_length(), nr, nr2);
}
static bool only_zero_bytes(const char *ptr, size_t length)
{
for (uint i= 0 ; i < length; i++)
{
if (ptr[i] != 0)
return false;
}
return true;
}
static ulong KEY_pack_flags(uint column_nr)
{
/*
Return zero by default. A particular data type can override
this method return some flags, e.g. HA_PACK_KEY to enable
key prefix compression.
*/
return 0;
}
/*
Convert from the in-record to the binlog representation.
Used in Field::pack(), and in filesort to store the addon fields.
By default, do what BINARY(N) does.
*/
static uchar *pack(uchar *to, const uchar *from, uint max_length)
{
return StringPack(&my_charset_bin, binary_length()).pack(to, from, max_length);
}
/*
Convert from the in-binary-log to the in-record representation.
Used in Field::unpack().
By default, do what BINARY(N) does.
*/
static const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
return StringPack(&my_charset_bin, binary_length()).unpack(to, from, from_end,
param_data);
}
};
#endif /* SQL_TYPE_FIXEDBIN_STORAGE */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment