MDEV-12668 SRID is not preserved in UNION, VIEW, MIN, MAX
Fixing the problem that an operation involving a mix of two or more GEOMETRY operands did not preserve their SRIDs. Now SRIDs are preserved by hybrid functions, subqueries, TVCs, UNIONs, VIEWs. Incompatible change: An attempt to mix two different SRIDs now raises an error. Details: - Adding a new class Type_extra_attributes. It's a generic container which can store very specific data type attributes. For now it can store one uint32 and one const pointer attribute (for GEOMETRY's SRID and for ENUM/SET TYPELIB respectively). In the future it can grow as needed. Type_extra_attributes will also be reused soon to store "const Type_zone*" pointers for the TIMESTAMP's "WITH TIME ZONE 'tz'" attribute (a timestamp data type with a fixed time zone independent from @@time_zone). The time zone attribute will be stored in exactly the same way like a TYPELIB pointer is stored by ENUM/SET. - Removing Column_definition_attributes members "interval" and "srid". Deriving Column_definition_attributes from the generic attribute container Type_extra_attributes instead. - Adding a new class Type_typelib_attributes, to store the TYPELIB of the ENUM and SET data types. Deriving Field_enum from it. Removing the member Field_enum::typelib. - Adding a new class Type_geom_attributes, to store the GEOMETRY related attributes. Deriving Field_geom from it. Removing the member Field_geom::srid. - Removing virtual methods: Field::get_typelib() Type_all_attributes::get_typelib() and Type_all_attributes::set_typelib() They were very specific to TYPELIB. Adding more generic virtual methods instead: * Field::type_extra_attributes() - to get extra attributes * Type_all_attributes::type_extra_attributes() - to get extra attributes * Type_all_attributes::type_extra_attributes_addr() - to set extra attributes - Removing Item_type_holder::enum_set_typelib. Deriving Item_type_holder from the generic attribute container Type_extra_attributes instead. This makes it possible for UNION to preserve SRID (in addition to preserving TYPELIB). - Deriving Item_hybrid_func from Type_extra_attributes. This makes it possible for hybrid functions (e.g. CASE, COALESCE, LEAST, GREATEST etc) to preserve SRID. - Deriving Item_singlerow_subselect from Type_extra_attributes and overriding methods: * Item_cache::type_extra_attributes() * subselect_single_select_engine::fix_length_and_dec() * Item_singlerow_subselect::type_extra_attributes() * Item_singlerow_subselect::type_extra_attributes_addr() This is needed to preserve SRID in subqueries and TVCs - Cleanup: fixing the data type of members * Binlog_type_info::m_enum_typelib * Binlog_type_info::m_set_typelib from "TYPELIB *" to "const TYPELIB *"
This commit is contained in:
parent
486d42d812
commit
001f93df2b
@ -5437,3 +5437,69 @@ DROP TABLE t1;
|
||||
#
|
||||
# End of 10.5 tests
|
||||
#
|
||||
#
|
||||
# Start of 11.5 tests
|
||||
#
|
||||
#
|
||||
# MDEV-12668 SRID is not preserved in UNION, VIEW, MIN, MAX
|
||||
#
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=0, b POINT REF_SYSTEM_ID=102);
|
||||
SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
ERROR HY000: Illegal parameter data types `point ref_system_id=0` and `point ref_system_id=102` for operation 'UNION'
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=101, b POINT REF_SYSTEM_ID=102);
|
||||
SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
ERROR HY000: Illegal parameter data types `point ref_system_id=101` and `point ref_system_id=102` for operation 'UNION'
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=101, b POINT REF_SYSTEM_ID=101);
|
||||
CREATE TABLE t2 AS SELECT a, b FROM t1;
|
||||
CREATE VIEW v2 AS SELECT a, b FROM t1;
|
||||
CREATE TABLE t3 AS SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
CREATE VIEW v3 AS SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
CREATE TABLE t4 AS SELECT COALESCE(a,b) AS a FROM t1;
|
||||
CREATE VIEW v4 AS SELECT COALESCE(a,b) AS a FROM t1;
|
||||
CREATE TABLE t5 AS SELECT LEAST(a,b) AS a FROM t1;
|
||||
CREATE VIEW v5 AS SELECT LEAST(a,b) AS a FROM t1;
|
||||
CREATE TABLE t6 AS SELECT MIN(a) AS a FROM t1;
|
||||
CREATE VIEW v6 AS SELECT MIN(a) AS a FROM t1;
|
||||
CREATE TABLE t7 AS SELECT MIN(a) AS a FROM t1 UNION SELECT b FROM v2;
|
||||
CREATE VIEW v7 AS SELECT MIN(a) AS a FROM t1 UNION SELECT b FROM v2;
|
||||
CREATE TABLE t8 AS SELECT (SELECT a FROM t1) AS a;
|
||||
CREATE VIEW v8 AS SELECT (SELECT a FROM t1) AS a;
|
||||
CREATE TABLE t9 AS VALUES ((SELECT MAX(a) FROM t1)),(((SELECT MIN(b) FROM t1)));
|
||||
CREATE VIEW v9 AS VALUES ((SELECT MAX(a) FROM t1)),(((SELECT MIN(b) FROM t1)));
|
||||
SELECT
|
||||
G_TABLE_NAME, G_GEOMETRY_COLUMN, SRID
|
||||
FROM
|
||||
INFORMATION_SCHEMA.GEOMETRY_COLUMNS
|
||||
WHERE
|
||||
F_TABLE_SCHEMA='test'
|
||||
ORDER
|
||||
BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
|
||||
G_TABLE_NAME G_GEOMETRY_COLUMN SRID
|
||||
t1 a 101
|
||||
t1 b 101
|
||||
t2 a 101
|
||||
t2 b 101
|
||||
t3 a 101
|
||||
t4 a 101
|
||||
t5 a 101
|
||||
t6 a 101
|
||||
t7 a 101
|
||||
t8 a 101
|
||||
t9 (SELECT MAX(a) FROM t1) 101
|
||||
v2 a 101
|
||||
v2 b 101
|
||||
v3 a 101
|
||||
v4 a 101
|
||||
v5 a 101
|
||||
v6 a 101
|
||||
v7 a 101
|
||||
v8 a 101
|
||||
v9 (select max(`test`.`t1`.`a`) from `test`.`t1`) 101
|
||||
DROP TABLE t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
DROP VIEW v2,v3,v4,v5,v6,v7,v8,v9;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# End of 11.5 tests
|
||||
#
|
||||
|
@ -3441,3 +3441,64 @@ DROP TABLE t1;
|
||||
--echo #
|
||||
--echo # End of 10.5 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # Start of 11.5 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-12668 SRID is not preserved in UNION, VIEW, MIN, MAX
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=0, b POINT REF_SYSTEM_ID=102);
|
||||
--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
|
||||
SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=101, b POINT REF_SYSTEM_ID=102);
|
||||
--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
|
||||
SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t1 (a POINT REF_SYSTEM_ID=101, b POINT REF_SYSTEM_ID=101);
|
||||
|
||||
CREATE TABLE t2 AS SELECT a, b FROM t1;
|
||||
CREATE VIEW v2 AS SELECT a, b FROM t1;
|
||||
|
||||
CREATE TABLE t3 AS SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
CREATE VIEW v3 AS SELECT a FROM t1 UNION SELECT b FROM t1;
|
||||
|
||||
CREATE TABLE t4 AS SELECT COALESCE(a,b) AS a FROM t1;
|
||||
CREATE VIEW v4 AS SELECT COALESCE(a,b) AS a FROM t1;
|
||||
|
||||
CREATE TABLE t5 AS SELECT LEAST(a,b) AS a FROM t1;
|
||||
CREATE VIEW v5 AS SELECT LEAST(a,b) AS a FROM t1;
|
||||
|
||||
CREATE TABLE t6 AS SELECT MIN(a) AS a FROM t1;
|
||||
CREATE VIEW v6 AS SELECT MIN(a) AS a FROM t1;
|
||||
|
||||
CREATE TABLE t7 AS SELECT MIN(a) AS a FROM t1 UNION SELECT b FROM v2;
|
||||
CREATE VIEW v7 AS SELECT MIN(a) AS a FROM t1 UNION SELECT b FROM v2;
|
||||
|
||||
CREATE TABLE t8 AS SELECT (SELECT a FROM t1) AS a;
|
||||
CREATE VIEW v8 AS SELECT (SELECT a FROM t1) AS a;
|
||||
|
||||
CREATE TABLE t9 AS VALUES ((SELECT MAX(a) FROM t1)),(((SELECT MIN(b) FROM t1)));
|
||||
CREATE VIEW v9 AS VALUES ((SELECT MAX(a) FROM t1)),(((SELECT MIN(b) FROM t1)));
|
||||
|
||||
SELECT
|
||||
G_TABLE_NAME, G_GEOMETRY_COLUMN, SRID
|
||||
FROM
|
||||
INFORMATION_SCHEMA.GEOMETRY_COLUMNS
|
||||
WHERE
|
||||
F_TABLE_SCHEMA='test'
|
||||
ORDER
|
||||
BY G_TABLE_NAME, G_GEOMETRY_COLUMN;
|
||||
|
||||
DROP TABLE t2,t3,t4,t5,t6,t7,t8,t9;
|
||||
DROP VIEW v2,v3,v4,v5,v6,v7,v8,v9;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # End of 11.5 tests
|
||||
--echo #
|
||||
|
76
sql/field.cc
76
sql/field.cc
@ -9341,7 +9341,7 @@ int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
|
||||
|
||||
/* Remove end space */
|
||||
length= (uint) field_charset()->lengthsp(from, length);
|
||||
uint tmp=find_type2(typelib, from, length, field_charset());
|
||||
uint tmp=find_type2(m_typelib, from, length, field_charset());
|
||||
if (!tmp)
|
||||
{
|
||||
if (length < 6) // Can't be more than 99999 enums
|
||||
@ -9349,7 +9349,7 @@ int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
|
||||
/* This is for reading numbers with LOAD DATA INFILE */
|
||||
char *end;
|
||||
tmp=(uint) cs->strntoul(from,length,10,&end,&err);
|
||||
if (err || end != from+length || tmp > typelib->count)
|
||||
if (err || end != from + length || tmp > m_typelib->count)
|
||||
{
|
||||
tmp=0;
|
||||
set_warning(WARN_DATA_TRUNCATED, 1);
|
||||
@ -9379,7 +9379,7 @@ int Field_enum::store(longlong nr, bool unsigned_val)
|
||||
{
|
||||
DBUG_ASSERT(marked_for_write_or_computed());
|
||||
int error= 0;
|
||||
if ((ulonglong) nr > typelib->count || nr == 0)
|
||||
if ((ulonglong) nr > m_typelib->count || nr == 0)
|
||||
{
|
||||
set_warning(WARN_DATA_TRUNCATED, 1);
|
||||
if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION)
|
||||
@ -9425,7 +9425,7 @@ Binlog_type_info Field_enum::binlog_type_info() const
|
||||
{
|
||||
DBUG_ASSERT(Field_enum::type() == binlog_type());
|
||||
return Binlog_type_info(Field_enum::type(), real_type() + (pack_length() << 8),
|
||||
2, charset(), (TYPELIB *)get_typelib(), NULL);
|
||||
2, charset(), m_typelib, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -9433,12 +9433,12 @@ String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
|
||||
String *val_ptr)
|
||||
{
|
||||
uint tmp=(uint) Field_enum::val_int();
|
||||
if (!tmp || tmp > typelib->count)
|
||||
if (!tmp || tmp > m_typelib->count)
|
||||
val_ptr->set("", 0, field_charset());
|
||||
else
|
||||
val_ptr->set((const char*) typelib->type_names[tmp-1],
|
||||
typelib->type_lengths[tmp-1],
|
||||
field_charset());
|
||||
val_ptr->set((const char*) m_typelib->type_names[tmp - 1],
|
||||
m_typelib->type_lengths[tmp - 1],
|
||||
field_charset());
|
||||
return val_ptr;
|
||||
}
|
||||
|
||||
@ -9470,8 +9470,8 @@ void Field_enum::sql_type(String &res) const
|
||||
res.append(STRING_WITH_LEN("enum("));
|
||||
|
||||
bool flag=0;
|
||||
uint *len= typelib->type_lengths;
|
||||
for (const char **pos= typelib->type_names; *pos; pos++, len++)
|
||||
uint *len= m_typelib->type_lengths;
|
||||
for (const char **pos= m_typelib->type_names; *pos; pos++, len++)
|
||||
{
|
||||
uint dummy_errors;
|
||||
if (flag)
|
||||
@ -9491,7 +9491,7 @@ Field *Field_enum::make_new_field(MEM_ROOT *root, TABLE *new_table,
|
||||
Field_enum *res= (Field_enum*) Field::make_new_field(root, new_table,
|
||||
keep_type);
|
||||
if (res)
|
||||
res->typelib= copy_typelib(root, typelib);
|
||||
res->m_typelib= copy_typelib(root, m_typelib);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -9524,7 +9524,7 @@ int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
|
||||
from= tmpstr.ptr();
|
||||
length= tmpstr.length();
|
||||
}
|
||||
ulonglong tmp= find_set(typelib, from, length, field_charset(),
|
||||
ulonglong tmp= find_set(m_typelib, from, length, field_charset(),
|
||||
¬_used, ¬_used2, &got_warning);
|
||||
if (!tmp && length && length < 22)
|
||||
{
|
||||
@ -9552,10 +9552,10 @@ int Field_set::store(longlong nr, bool unsigned_val)
|
||||
int error= 0;
|
||||
ulonglong max_nr;
|
||||
|
||||
if (sizeof(ulonglong)*8 <= typelib->count)
|
||||
if (sizeof(ulonglong) * 8 <= m_typelib->count)
|
||||
max_nr= ULONGLONG_MAX;
|
||||
else
|
||||
max_nr= (1ULL << typelib->count) - 1;
|
||||
max_nr= (1ULL << m_typelib->count) - 1;
|
||||
|
||||
if ((ulonglong) nr > max_nr)
|
||||
{
|
||||
@ -9577,13 +9577,13 @@ String *Field_set::val_str(String *val_buffer,
|
||||
val_buffer->set_charset(field_charset());
|
||||
val_buffer->length(0);
|
||||
|
||||
while (tmp && bitnr < (uint) typelib->count)
|
||||
while (tmp && bitnr < (uint) m_typelib->count)
|
||||
{
|
||||
if (tmp & 1)
|
||||
{
|
||||
if (val_buffer->length())
|
||||
val_buffer->append(&field_separator, 1, &my_charset_latin1);
|
||||
String str(typelib->type_names[bitnr], typelib->type_lengths[bitnr],
|
||||
String str(m_typelib->type_names[bitnr], m_typelib->type_lengths[bitnr],
|
||||
field_charset());
|
||||
val_buffer->append(str);
|
||||
}
|
||||
@ -9603,8 +9603,8 @@ void Field_set::sql_type(String &res) const
|
||||
res.append(STRING_WITH_LEN("set("));
|
||||
|
||||
bool flag=0;
|
||||
uint *len= typelib->type_lengths;
|
||||
for (const char **pos= typelib->type_names; *pos; pos++, len++)
|
||||
uint *len= m_typelib->type_lengths;
|
||||
for (const char **pos= m_typelib->type_names; *pos; pos++, len++)
|
||||
{
|
||||
uint dummy_errors;
|
||||
if (flag)
|
||||
@ -9621,7 +9621,7 @@ Binlog_type_info Field_set::binlog_type_info() const
|
||||
{
|
||||
DBUG_ASSERT(Field_set::type() == binlog_type());
|
||||
return Binlog_type_info(Field_set::type(), real_type()
|
||||
+ (pack_length() << 8), 2, charset(), NULL, (TYPELIB *)get_typelib());
|
||||
+ (pack_length() << 8), 2, charset(), NULL, m_typelib);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -9668,13 +9668,13 @@ bool Field_enum::eq_def(const Field *field) const
|
||||
if (!Field::eq_def(field))
|
||||
return FALSE;
|
||||
|
||||
values= ((Field_enum*) field)->typelib;
|
||||
values= ((Field_enum*) field)->m_typelib;
|
||||
|
||||
/* Definition must be strictly equal. */
|
||||
if (typelib->count != values->count)
|
||||
if (m_typelib->count != values->count)
|
||||
return FALSE;
|
||||
|
||||
return compare_type_names(field_charset(), typelib, values);
|
||||
return compare_type_names(field_charset(), m_typelib, values);
|
||||
}
|
||||
|
||||
|
||||
@ -9689,8 +9689,6 @@ bool Field_enum::eq_def(const Field *field) const
|
||||
|
||||
bool Field_enum::is_equal(const Column_definition &new_field) const
|
||||
{
|
||||
const TYPELIB *values= new_field.interval;
|
||||
|
||||
/*
|
||||
The fields are compatible if they have the same flags,
|
||||
type, charset and have the same underlying length.
|
||||
@ -9705,11 +9703,11 @@ bool Field_enum::is_equal(const Column_definition &new_field) const
|
||||
enumeration or set members to the end of the list of valid member
|
||||
values only alters table metadata and not table data.
|
||||
*/
|
||||
if (typelib->count > values->count)
|
||||
if (m_typelib->count > new_field.typelib()->count)
|
||||
return false;
|
||||
|
||||
/* Check whether there are modification before the end. */
|
||||
if (! compare_type_names(field_charset(), typelib, new_field.interval))
|
||||
if (! compare_type_names(field_charset(), m_typelib, new_field.typelib()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -10405,9 +10403,10 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
|
||||
bool reuse_interval_list_values)
|
||||
{
|
||||
DBUG_ENTER("Column_definition::create_interval_from_interval_list");
|
||||
DBUG_ASSERT(!interval);
|
||||
DBUG_ASSERT(!typelib());
|
||||
TYPELIB *tmpint;
|
||||
if (!(interval= tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
|
||||
if (!set_typelib(tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))).
|
||||
typelib())
|
||||
DBUG_RETURN(true); // EOM
|
||||
|
||||
List_iterator<String> it(interval_list);
|
||||
@ -10476,7 +10475,8 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
|
||||
interval_list.empty(); // Don't need interval_list anymore
|
||||
DBUG_RETURN(false);
|
||||
err:
|
||||
interval= NULL; // Avoid having both non-empty interval_list and interval
|
||||
// Avoid having both non-empty interval_list and the typelib attribute
|
||||
set_typelib(NULL);
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
@ -10504,7 +10504,7 @@ bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
|
||||
as the parser requires at least one element, so for a ENUM or SET field it
|
||||
should never happen that both internal_list.elements and interval are 0.
|
||||
*/
|
||||
DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0));
|
||||
DBUG_ASSERT((typelib() == NULL) == (interval_list.elements > 0));
|
||||
|
||||
/*
|
||||
Create typelib from interval_list, and if necessary
|
||||
@ -10527,8 +10527,8 @@ bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
|
||||
as the original field can be freed before the end of the life
|
||||
cycle of "this".
|
||||
*/
|
||||
DBUG_ASSERT(interval);
|
||||
if (!(interval= copy_typelib(mem_root, interval)))
|
||||
DBUG_ASSERT(typelib());
|
||||
if (!set_typelib(copy_typelib(mem_root, typelib())).typelib())
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
prepare_interval_field_calc_length();
|
||||
@ -10886,7 +10886,7 @@ Field *Column_definition_attributes::make_field(TABLE_SHARE *share,
|
||||
{
|
||||
DBUG_ASSERT(length <= UINT_MAX32);
|
||||
DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
|
||||
handler->name().ptr(), (uint) length, interval,
|
||||
handler->name().ptr(), (uint) length, typelib(),
|
||||
FLAGSTR(pack_flag, FIELDFLAG_BINARY),
|
||||
FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
|
||||
FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
|
||||
@ -10915,9 +10915,7 @@ bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item)
|
||||
|
||||
Column_definition_attributes::Column_definition_attributes(const Field *field)
|
||||
:length(field->character_octet_length() / field->charset()->mbmaxlen),
|
||||
interval(NULL),
|
||||
charset(field->charset()), // May be NULL ptr
|
||||
srid(0),
|
||||
pack_flag(0),
|
||||
decimals(field->decimals()),
|
||||
unireg_check(field->unireg_check)
|
||||
@ -10926,10 +10924,9 @@ Column_definition_attributes::Column_definition_attributes(const Field *field)
|
||||
|
||||
Column_definition_attributes::
|
||||
Column_definition_attributes(const Type_all_attributes &attr)
|
||||
:length(attr.max_length),
|
||||
interval(attr.get_typelib()),
|
||||
:Type_extra_attributes(attr.type_extra_attributes()),
|
||||
length(attr.max_length),
|
||||
charset(attr.collation.collation),
|
||||
srid(0),
|
||||
pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL),
|
||||
decimals(attr.decimals),
|
||||
unireg_check(Field::NONE)
|
||||
@ -10942,7 +10939,6 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
|
||||
Field *orig_field)
|
||||
:Column_definition_attributes(old_field)
|
||||
{
|
||||
srid= 0;
|
||||
on_update= NULL;
|
||||
field_name= old_field->field_name;
|
||||
flags= old_field->flags;
|
||||
@ -11034,6 +11030,7 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field,
|
||||
const handler *file)
|
||||
{
|
||||
set_handler(dup_field->type_handler());
|
||||
Type_extra_attributes::operator=(*dup_field);
|
||||
default_value= dup_field->default_value;
|
||||
DBUG_ASSERT(dup_field->charset); // Set by prepare_stage1()
|
||||
charset= dup_field->charset;
|
||||
@ -11042,7 +11039,6 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field,
|
||||
decimals= dup_field->decimals;
|
||||
unireg_check= dup_field->unireg_check;
|
||||
flags= dup_field->flags;
|
||||
interval= dup_field->interval;
|
||||
vcol_info= dup_field->vcol_info;
|
||||
invisible= dup_field->invisible;
|
||||
check_constraint= dup_field->check_constraint;
|
||||
|
38
sql/field.h
38
sql/field.h
@ -662,8 +662,8 @@ public:
|
||||
Retrieve the field metadata for fields.
|
||||
*/
|
||||
CHARSET_INFO *m_cs; // NULL if not relevant
|
||||
TYPELIB *m_enum_typelib; // NULL if not relevant
|
||||
TYPELIB *m_set_typelib; // NULL if not relevant
|
||||
const TYPELIB *m_enum_typelib; // NULL if not relevant
|
||||
const TYPELIB *m_set_typelib; // NULL if not relevant
|
||||
binlog_sign_t m_signedness;
|
||||
uint16 m_metadata;
|
||||
uint8 m_metadata_size;
|
||||
@ -708,7 +708,7 @@ public:
|
||||
Binlog_type_info(uchar type_code, uint16 metadata,
|
||||
uint8 metadata_size,
|
||||
CHARSET_INFO *cs,
|
||||
TYPELIB *t_enum, TYPELIB *t_set)
|
||||
const TYPELIB *t_enum, const TYPELIB *t_set)
|
||||
:m_cs(cs),
|
||||
m_enum_typelib(t_enum),
|
||||
m_set_typelib(t_set),
|
||||
@ -1661,7 +1661,10 @@ public:
|
||||
virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
|
||||
virtual longlong val_datetime_packed(THD *thd);
|
||||
virtual longlong val_time_packed(THD *thd);
|
||||
virtual const TYPELIB *get_typelib() const { return NULL; }
|
||||
virtual const Type_extra_attributes type_extra_attributes() const
|
||||
{
|
||||
return Type_extra_attributes();
|
||||
}
|
||||
virtual CHARSET_INFO *charset() const= 0;
|
||||
/* returns TRUE if the new charset differs. */
|
||||
virtual void change_charset(const DTCollation &new_cs) {}
|
||||
@ -4778,7 +4781,9 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class Field_enum :public Field_str {
|
||||
class Field_enum :public Field_str,
|
||||
public Type_typelib_attributes
|
||||
{
|
||||
static void do_field_enum(const Copy_field *copy_field);
|
||||
longlong val_int(const uchar *) const;
|
||||
Data_type_compatibility can_optimize_range_or_keypart_ref(
|
||||
@ -4787,7 +4792,6 @@ class Field_enum :public Field_str {
|
||||
protected:
|
||||
uint packlength;
|
||||
public:
|
||||
const TYPELIB *typelib;
|
||||
Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
|
||||
uchar null_bit_arg,
|
||||
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
|
||||
@ -4796,7 +4800,8 @@ public:
|
||||
const DTCollation &collation)
|
||||
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
||||
unireg_check_arg, field_name_arg, collation),
|
||||
packlength(packlength_arg),typelib(typelib_arg)
|
||||
Type_typelib_attributes(typelib_arg),
|
||||
packlength(packlength_arg)
|
||||
{
|
||||
flags|=ENUM_FLAG;
|
||||
}
|
||||
@ -4869,8 +4874,10 @@ public:
|
||||
/* enum and set are sorted as integers */
|
||||
CHARSET_INFO *sort_charset() const override { return &my_charset_bin; }
|
||||
decimal_digits_t decimals() const override { return 0; }
|
||||
const TYPELIB *get_typelib() const override { return typelib; }
|
||||
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return Type_extra_attributes(m_typelib);
|
||||
}
|
||||
uchar *pack(uchar *to, const uchar *from, uint max_length) override;
|
||||
const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
|
||||
uint param_data) override;
|
||||
@ -5198,7 +5205,7 @@ public:
|
||||
|
||||
extern const LEX_CSTRING null_clex_str;
|
||||
|
||||
class Column_definition_attributes
|
||||
class Column_definition_attributes: public Type_extra_attributes
|
||||
{
|
||||
public:
|
||||
/*
|
||||
@ -5206,17 +5213,13 @@ public:
|
||||
max number of characters.
|
||||
*/
|
||||
ulonglong length;
|
||||
const TYPELIB *interval;
|
||||
CHARSET_INFO *charset;
|
||||
uint32 srid;
|
||||
uint32 pack_flag;
|
||||
decimal_digits_t decimals;
|
||||
Field::utype unireg_check;
|
||||
Column_definition_attributes()
|
||||
:length(0),
|
||||
interval(NULL),
|
||||
charset(&my_charset_bin),
|
||||
srid(0),
|
||||
pack_flag(0),
|
||||
decimals(0),
|
||||
unireg_check(Field::NONE)
|
||||
@ -5287,7 +5290,7 @@ class Column_definition: public Sql_alloc,
|
||||
const char **pos;
|
||||
uint *len;
|
||||
*max_length= *tot_length= 0;
|
||||
for (pos= interval->type_names, len= interval->type_lengths;
|
||||
for (pos= typelib()->type_names, len= typelib()->type_lengths;
|
||||
*pos ; pos++, len++)
|
||||
{
|
||||
size_t length= charset->numchars(*pos, *pos + *len);
|
||||
@ -5412,7 +5415,7 @@ public:
|
||||
if (real_field_type() == MYSQL_TYPE_SET)
|
||||
{
|
||||
calculate_interval_lengths(&dummy, &field_length);
|
||||
length= field_length + (interval->count - 1);
|
||||
length= field_length + (typelib()->count - 1);
|
||||
}
|
||||
else /* MYSQL_TYPE_ENUM */
|
||||
{
|
||||
@ -5516,15 +5519,14 @@ public:
|
||||
void set_type(const Column_definition &other)
|
||||
{
|
||||
set_handler(other.type_handler());
|
||||
Type_extra_attributes::operator=(other);
|
||||
length= other.length;
|
||||
char_length= other.char_length;
|
||||
decimals= other.decimals;
|
||||
flags= other.flags;
|
||||
pack_length= other.pack_length;
|
||||
unireg_check= other.unireg_check;
|
||||
interval= other.interval;
|
||||
charset= other.charset;
|
||||
srid= other.srid;
|
||||
pack_flag= other.pack_flag;
|
||||
}
|
||||
|
||||
|
52
sql/item.h
52
sql/item.h
@ -1328,7 +1328,14 @@ public:
|
||||
{
|
||||
return type_handler()->max_display_length(this);
|
||||
}
|
||||
const TYPELIB *get_typelib() const override { return NULL; }
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return Type_extra_attributes();
|
||||
}
|
||||
Type_extra_attributes *type_extra_attributes_addr() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
/* optimized setting of maybe_null without jumps. Minimizes code size */
|
||||
inline void set_maybe_null(bool maybe_null_arg)
|
||||
{
|
||||
@ -1366,11 +1373,6 @@ public:
|
||||
bool is_top_level_item() const
|
||||
{ return (bool) (base_flags & item_base_t::AT_TOP_LEVEL); }
|
||||
|
||||
void set_typelib(const TYPELIB *typelib) override
|
||||
{
|
||||
// Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet.
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
Item_cache* get_cache(THD *thd) const
|
||||
{
|
||||
return type_handler()->Item_get_cache(thd, this);
|
||||
@ -3696,7 +3698,10 @@ public:
|
||||
Field *create_tmp_field_ex(MEM_ROOT *root,
|
||||
TABLE *table, Tmp_field_src *src,
|
||||
const Tmp_field_param *param) override;
|
||||
const TYPELIB *get_typelib() const override { return field->get_typelib(); }
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return field->type_extra_attributes();
|
||||
}
|
||||
enum_monotonicity_info get_monotonicity_info() const override
|
||||
{
|
||||
return MONOTONIC_STRICT_INCREASING;
|
||||
@ -5731,9 +5736,9 @@ public:
|
||||
{
|
||||
return ref ? (*ref)->real_item() : this;
|
||||
}
|
||||
const TYPELIB *get_typelib() const override
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return ref ? (*ref)->get_typelib() : NULL;
|
||||
return ref ? (*ref)->type_extra_attributes() : Type_extra_attributes();
|
||||
}
|
||||
|
||||
bool walk(Item_processor processor, bool walk_subquery, void *arg) override
|
||||
@ -7154,6 +7159,12 @@ public:
|
||||
|
||||
const Type_handler *type_handler() const override
|
||||
{ return Type_handler_hybrid_field_type::type_handler(); }
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
DBUG_ASSERT(fixed());
|
||||
return example ? example->type_extra_attributes() :
|
||||
Type_extra_attributes();
|
||||
}
|
||||
Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
|
||||
const Tmp_field_param *param) override
|
||||
{
|
||||
@ -7677,15 +7688,17 @@ public:
|
||||
Item_type_holder do not need cleanup() because its time of live limited by
|
||||
single SP/PS execution.
|
||||
*/
|
||||
class Item_type_holder: public Item, public Type_handler_hybrid_field_type
|
||||
class Item_type_holder: public Item,
|
||||
public Type_handler_hybrid_field_type,
|
||||
public Type_extra_attributes
|
||||
{
|
||||
protected:
|
||||
const TYPELIB *enum_set_typelib;
|
||||
public:
|
||||
Item_type_holder(THD *thd, Item *item, const Type_handler *handler,
|
||||
const Type_all_attributes *attr, bool maybe_null_arg)
|
||||
:Item(thd), Type_handler_hybrid_field_type(handler),
|
||||
enum_set_typelib(attr->get_typelib())
|
||||
const Type_all_attributes *attr,
|
||||
bool maybe_null_arg)
|
||||
:Item(thd),
|
||||
Type_handler_hybrid_field_type(handler),
|
||||
Type_extra_attributes(attr->type_extra_attributes())
|
||||
{
|
||||
name= item->name;
|
||||
Type_std_attributes::set(*attr);
|
||||
@ -7705,7 +7718,14 @@ public:
|
||||
}
|
||||
|
||||
Type type() const override { return TYPE_HOLDER; }
|
||||
const TYPELIB *get_typelib() const override { return enum_set_typelib; }
|
||||
Type_extra_attributes *type_extra_attributes_addr() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
/*
|
||||
When handling a query like this:
|
||||
VALUES ('') UNION VALUES( _utf16 0x0020 COLLATE utf16_bin);
|
||||
|
@ -3295,8 +3295,8 @@ bool Item_func_find_in_set::fix_length_and_dec(THD *thd)
|
||||
{
|
||||
// find is not NULL pointer so args[0] is not a null-value
|
||||
DBUG_ASSERT(!args[0]->null_value);
|
||||
enum_value= find_type(((Field_enum*) field)->typelib,find->ptr(),
|
||||
find->length(), 0);
|
||||
enum_value= find_type(((Field_enum*) field)->typelib(), find->ptr(),
|
||||
find->length(), 0);
|
||||
enum_bit=0;
|
||||
if (enum_value)
|
||||
enum_bit= 1ULL << (enum_value-1);
|
||||
|
@ -497,7 +497,8 @@ public:
|
||||
Functions whose returned field type is determined at fix_fields() time.
|
||||
*/
|
||||
class Item_hybrid_func: public Item_func,
|
||||
public Type_handler_hybrid_field_type
|
||||
public Type_handler_hybrid_field_type,
|
||||
public Type_extra_attributes
|
||||
{
|
||||
protected:
|
||||
bool fix_attributes(Item **item, uint nitems);
|
||||
@ -512,6 +513,14 @@ public:
|
||||
:Item_func(thd, item), Type_handler_hybrid_field_type(item) { }
|
||||
const Type_handler *type_handler() const override
|
||||
{ return Type_handler_hybrid_field_type::type_handler(); }
|
||||
Type_extra_attributes *type_extra_attributes_addr() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
void fix_length_and_dec_long_or_longlong(uint char_length, bool unsigned_arg)
|
||||
{
|
||||
collation= DTCollation_numeric();
|
||||
|
@ -4037,6 +4037,8 @@ bool subselect_single_select_engine::fix_length_and_dec(Item_cache **row)
|
||||
if (set_row(select_lex->item_list, row))
|
||||
return TRUE;
|
||||
item->collation.set(row[0]->collation);
|
||||
if (Type_extra_attributes *eattr= item->type_extra_attributes_addr())
|
||||
eattr[0]= row[0]->type_extra_attributes();
|
||||
if (cols() != 1)
|
||||
maybe_null= 0;
|
||||
return FALSE;
|
||||
|
@ -294,7 +294,8 @@ public:
|
||||
/* single value subselect */
|
||||
|
||||
class Item_cache;
|
||||
class Item_singlerow_subselect :public Item_subselect
|
||||
class Item_singlerow_subselect :public Item_subselect,
|
||||
public Type_extra_attributes
|
||||
{
|
||||
protected:
|
||||
Item_cache *value, **row;
|
||||
@ -319,6 +320,14 @@ public:
|
||||
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
|
||||
const Type_handler *type_handler() const override;
|
||||
bool fix_length_and_dec() override;
|
||||
Type_extra_attributes *type_extra_attributes_addr() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint cols() const override;
|
||||
Item* element_index(uint i) override
|
||||
|
@ -1172,7 +1172,10 @@ public:
|
||||
{
|
||||
return get_arg(0)->real_type_handler();
|
||||
}
|
||||
const TYPELIB *get_typelib() const override { return args[0]->get_typelib(); }
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return args[0]->type_extra_attributes();
|
||||
}
|
||||
void update_field() override;
|
||||
void min_max_update_str_field();
|
||||
void min_max_update_real_field();
|
||||
|
@ -6248,7 +6248,7 @@ bool Table_map_log_event::init_column_name_field()
|
||||
bool Table_map_log_event::init_set_str_value_field()
|
||||
{
|
||||
StringBuffer<1024> buf;
|
||||
TYPELIB *typelib;
|
||||
const TYPELIB *typelib;
|
||||
|
||||
/*
|
||||
SET string values are stored in the same format:
|
||||
@ -6278,7 +6278,7 @@ bool Table_map_log_event::init_set_str_value_field()
|
||||
bool Table_map_log_event::init_enum_str_value_field()
|
||||
{
|
||||
StringBuffer<1024> buf;
|
||||
TYPELIB *typelib;
|
||||
const TYPELIB *typelib;
|
||||
|
||||
/* ENUM is same to SET columns, see comment in init_set_str_value_field */
|
||||
for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
|
||||
|
@ -863,7 +863,7 @@ class Grant_table_base
|
||||
{
|
||||
Field *field= m_table->field[end_priv_columns];
|
||||
if (field->real_type() == MYSQL_TYPE_ENUM &&
|
||||
static_cast<Field_enum*>(field)->typelib->count == 2)
|
||||
static_cast<Field_enum*>(field)->typelib()->count == 2)
|
||||
{
|
||||
if (!start_priv_columns)
|
||||
start_priv_columns= end_priv_columns;
|
||||
@ -3052,7 +3052,7 @@ static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field)
|
||||
|
||||
for (pos=form->field+fieldnr, bit=1;
|
||||
*pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
|
||||
((Field_enum*) (*pos))->typelib->count == 2 ;
|
||||
((Field_enum*) (*pos))->typelib()->count == 2 ;
|
||||
pos++, fieldnr++, bit<<=1)
|
||||
{
|
||||
if (get_YN_as_bool(*pos))
|
||||
|
@ -8197,20 +8197,22 @@ public:
|
||||
class Type_holder: public Sql_alloc,
|
||||
public Item_args,
|
||||
public Type_handler_hybrid_field_type,
|
||||
public Type_all_attributes
|
||||
public Type_all_attributes,
|
||||
public Type_extra_attributes
|
||||
{
|
||||
const TYPELIB *m_typelib;
|
||||
bool m_maybe_null;
|
||||
public:
|
||||
Type_holder()
|
||||
:m_typelib(NULL),
|
||||
m_maybe_null(false)
|
||||
:m_maybe_null(false)
|
||||
{ }
|
||||
|
||||
void set_type_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; }
|
||||
void set_type_maybe_null(bool maybe_null_arg) override
|
||||
{
|
||||
m_maybe_null= maybe_null_arg;
|
||||
}
|
||||
bool get_maybe_null() const { return m_maybe_null; }
|
||||
|
||||
decimal_digits_t decimal_precision() const
|
||||
decimal_digits_t decimal_precision() const override
|
||||
{
|
||||
/*
|
||||
Type_holder is not used directly to create fields, so
|
||||
@ -8222,13 +8224,13 @@ public:
|
||||
DBUG_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
void set_typelib(const TYPELIB *typelib)
|
||||
Type_extra_attributes *type_extra_attributes_addr() override
|
||||
{
|
||||
m_typelib= typelib;
|
||||
return this;
|
||||
}
|
||||
const TYPELIB *get_typelib() const
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
return m_typelib;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool aggregate_attributes(THD *thd)
|
||||
|
@ -2269,7 +2269,7 @@ bool Column_definition::prepare_stage2_typelib(const char *type_name,
|
||||
pack_flag= pack_length_to_packflag(pack_length) | field_flags;
|
||||
if (charset->state & MY_CS_BINSORT)
|
||||
pack_flag|= FIELDFLAG_BINARY;
|
||||
return check_duplicates_in_interval(type_name, field_name.str, interval,
|
||||
return check_duplicates_in_interval(type_name, field_name.str, typelib(),
|
||||
charset, dup_val_count);
|
||||
}
|
||||
|
||||
@ -2537,13 +2537,13 @@ bool Column_definition::prepare_stage1_check_typelib_default()
|
||||
{
|
||||
char *not_used;
|
||||
uint not_used2;
|
||||
find_set(interval, def->ptr(), def->length(),
|
||||
find_set(typelib(), def->ptr(), def->length(),
|
||||
charset, ¬_used, ¬_used2, ¬_found);
|
||||
}
|
||||
else /* MYSQL_TYPE_ENUM */
|
||||
{
|
||||
def->length(charset->lengthsp(def->ptr(), def->length()));
|
||||
not_found= !find_type2(interval, def->ptr(), def->length(), charset);
|
||||
not_found= !find_type2(typelib(), def->ptr(), def->length(), charset);
|
||||
}
|
||||
}
|
||||
if (not_found)
|
||||
|
@ -2666,7 +2666,7 @@ Field *Type_handler_enum::make_conversion_table_field(MEM_ROOT *root,
|
||||
Field_enum(NULL, target->field_length,
|
||||
(uchar *) "", 1, Field::NONE, &empty_clex_str,
|
||||
metadata & 0x00ff/*pack_length()*/,
|
||||
((const Field_enum*) target)->typelib, target->charset());
|
||||
((const Field_enum*) target)->typelib(), target->charset());
|
||||
}
|
||||
|
||||
|
||||
@ -2682,7 +2682,7 @@ Field *Type_handler_set::make_conversion_table_field(MEM_ROOT *root,
|
||||
Field_set(NULL, target->field_length,
|
||||
(uchar *) "", 1, Field::NONE, &empty_clex_str,
|
||||
metadata & 0x00ff/*pack_length()*/,
|
||||
((const Field_enum*) target)->typelib, target->charset());
|
||||
((const Field_enum*) target)->typelib(), target->charset());
|
||||
}
|
||||
|
||||
|
||||
@ -2977,7 +2977,8 @@ void Type_handler_typelib::
|
||||
const Field *field) const
|
||||
{
|
||||
DBUG_ASSERT(def->flags & (ENUM_FLAG | SET_FLAG));
|
||||
def->interval= field->get_typelib();
|
||||
const Field_enum *field_enum= static_cast<const Field_enum*>(field);
|
||||
field_enum->Type_typelib_attributes::store(def);
|
||||
}
|
||||
|
||||
|
||||
@ -3336,7 +3337,7 @@ bool Type_handler_set::
|
||||
if (def->prepare_stage2_typelib("SET", FIELDFLAG_BITFIELD, &dup_count))
|
||||
return true;
|
||||
/* Check that count of unique members is not more then 64 */
|
||||
if (def->interval->count - dup_count > sizeof(longlong)*8)
|
||||
if (def->typelib()->count - dup_count > sizeof(longlong)*8)
|
||||
{
|
||||
my_error(ER_TOO_BIG_SET, MYF(0), def->field_name.str);
|
||||
return true;
|
||||
@ -3549,14 +3550,14 @@ Type_handler_string_result::calc_key_length(const Column_definition &def) const
|
||||
|
||||
uint Type_handler_enum::calc_key_length(const Column_definition &def) const
|
||||
{
|
||||
DBUG_ASSERT(def.interval);
|
||||
return get_enum_pack_length(def.interval->count);
|
||||
DBUG_ASSERT(def.typelib());
|
||||
return get_enum_pack_length(def.typelib()->count);
|
||||
}
|
||||
|
||||
uint Type_handler_set::calc_key_length(const Column_definition &def) const
|
||||
{
|
||||
DBUG_ASSERT(def.interval);
|
||||
return get_set_pack_length(def.interval->count);
|
||||
DBUG_ASSERT(def.typelib());
|
||||
return get_set_pack_length(def.typelib()->count);
|
||||
}
|
||||
|
||||
uint Type_handler_blob_common::calc_key_length(const Column_definition &def) const
|
||||
@ -3931,13 +3932,14 @@ Field *Type_handler_enum::make_table_field(MEM_ROOT *root,
|
||||
const Type_all_attributes &attr,
|
||||
TABLE_SHARE *share) const
|
||||
{
|
||||
const TYPELIB *typelib= attr.get_typelib();
|
||||
DBUG_ASSERT(typelib);
|
||||
const Type_typelib_attributes typelib_attr(attr.type_extra_attributes());
|
||||
DBUG_ASSERT(typelib_attr.typelib());
|
||||
return new (root)
|
||||
Field_enum(addr.ptr(), attr.max_length,
|
||||
addr.null_ptr(), addr.null_bit(),
|
||||
Field::NONE, name,
|
||||
get_enum_pack_length(typelib->count), typelib,
|
||||
get_enum_pack_length(typelib_attr.typelib()->count),
|
||||
typelib_attr.typelib(),
|
||||
attr.collation);
|
||||
}
|
||||
|
||||
@ -3949,13 +3951,14 @@ Field *Type_handler_set::make_table_field(MEM_ROOT *root,
|
||||
TABLE_SHARE *share) const
|
||||
|
||||
{
|
||||
const TYPELIB *typelib= attr.get_typelib();
|
||||
DBUG_ASSERT(typelib);
|
||||
const Type_typelib_attributes typelib_attr(attr.type_extra_attributes());
|
||||
DBUG_ASSERT(typelib_attr.typelib());
|
||||
return new (root)
|
||||
Field_set(addr.ptr(), attr.max_length,
|
||||
addr.null_ptr(), addr.null_bit(),
|
||||
Field::NONE, name,
|
||||
get_enum_pack_length(typelib->count), typelib,
|
||||
get_enum_pack_length(typelib_attr.typelib()->count),
|
||||
typelib_attr.typelib(),
|
||||
attr.collation);
|
||||
}
|
||||
|
||||
@ -4694,8 +4697,8 @@ bool Type_handler_typelib::
|
||||
const TYPELIB *typelib= NULL;
|
||||
for (uint i= 0; i < nitems; i++)
|
||||
{
|
||||
const TYPELIB *typelib2;
|
||||
if ((typelib2= items[i]->get_typelib()))
|
||||
const Type_extra_attributes eattr2= items[i]->type_extra_attributes();
|
||||
if (eattr2.typelib())
|
||||
{
|
||||
if (typelib)
|
||||
{
|
||||
@ -4707,11 +4710,13 @@ bool Type_handler_typelib::
|
||||
handler->set_handler(&type_handler_varchar);
|
||||
return func->aggregate_attributes_string(func_name, items, nitems);
|
||||
}
|
||||
typelib= typelib2;
|
||||
typelib= eattr2.typelib();
|
||||
}
|
||||
}
|
||||
DBUG_ASSERT(typelib); // There must be at least one typelib
|
||||
func->set_typelib(typelib);
|
||||
Type_extra_attributes *eattr_addr= func->type_extra_attributes_addr();
|
||||
if (eattr_addr)
|
||||
eattr_addr->set_typelib(typelib);
|
||||
return func->aggregate_attributes_string(func_name, items, nitems);
|
||||
}
|
||||
|
||||
@ -8536,7 +8541,7 @@ Field *Type_handler_enum::
|
||||
return new (mem_root)
|
||||
Field_enum(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
|
||||
attr->unireg_check, name, attr->pack_flag_to_pack_length(),
|
||||
attr->interval, attr->charset);
|
||||
attr->typelib(), attr->charset);
|
||||
}
|
||||
|
||||
|
||||
@ -8550,7 +8555,7 @@ Field *Type_handler_set::
|
||||
return new (mem_root)
|
||||
Field_set(rec.ptr(), (uint32) attr->length, rec.null_ptr(), rec.null_bit(),
|
||||
attr->unireg_check, name, attr->pack_flag_to_pack_length(),
|
||||
attr->interval, attr->charset);
|
||||
attr->typelib(), attr->charset);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3402,6 +3402,98 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
A container for very specific data type attributes.
|
||||
For now it prodives space for:
|
||||
- one const pointer attributes
|
||||
- one unt32 attribute
|
||||
*/
|
||||
class Type_extra_attributes
|
||||
{
|
||||
const void *m_attr_const_void_ptr[1];
|
||||
uint32 m_attr_uint32[1];
|
||||
public:
|
||||
Type_extra_attributes()
|
||||
:m_attr_const_void_ptr{0},
|
||||
m_attr_uint32{0}
|
||||
{ }
|
||||
Type_extra_attributes(const void *const_void_ptr)
|
||||
:m_attr_const_void_ptr{const_void_ptr},
|
||||
m_attr_uint32{0}
|
||||
{ }
|
||||
/*
|
||||
Generic const pointer attributes.
|
||||
The ENUM and SET data types store TYPELIB information here.
|
||||
*/
|
||||
Type_extra_attributes & set_attr_const_void_ptr(uint i, const void *value)
|
||||
{
|
||||
m_attr_const_void_ptr[i]= value;
|
||||
return *this;
|
||||
}
|
||||
const void *get_attr_const_void_ptr(uint i) const
|
||||
{
|
||||
return m_attr_const_void_ptr[i];
|
||||
}
|
||||
/*
|
||||
Generic uint32 attributes.
|
||||
The GEOMETRY data type stores SRID here.
|
||||
*/
|
||||
Type_extra_attributes & set_attr_uint32(uint i, uint32 value)
|
||||
{
|
||||
m_attr_uint32[i]= value;
|
||||
return *this;
|
||||
}
|
||||
uint32 get_attr_uint32(uint i) const
|
||||
{
|
||||
return m_attr_uint32[i];
|
||||
}
|
||||
/*
|
||||
Helper methods for TYPELIB attributes.
|
||||
They are mostly needed to simplify the code
|
||||
in Column_definition_attributes and Column_definition methods.
|
||||
Eventually we should move this code into Type_typelib_attributes
|
||||
and remove these methods.
|
||||
*/
|
||||
Type_extra_attributes & set_typelib(const TYPELIB *typelib)
|
||||
{
|
||||
return set_attr_const_void_ptr(0, typelib);
|
||||
}
|
||||
const TYPELIB *typelib() const
|
||||
{
|
||||
return (const TYPELIB*) get_attr_const_void_ptr(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Type_typelib_attributes
|
||||
{
|
||||
protected:
|
||||
const TYPELIB *m_typelib;
|
||||
public:
|
||||
Type_typelib_attributes()
|
||||
:m_typelib(nullptr)
|
||||
{ }
|
||||
Type_typelib_attributes(const TYPELIB *typelib)
|
||||
:m_typelib(typelib)
|
||||
{ }
|
||||
Type_typelib_attributes(const Type_extra_attributes &eattr)
|
||||
:m_typelib((const TYPELIB *) eattr.get_attr_const_void_ptr(0))
|
||||
{ }
|
||||
void store(Type_extra_attributes *to) const
|
||||
{
|
||||
to->set_attr_const_void_ptr(0, m_typelib);
|
||||
}
|
||||
const TYPELIB *typelib() const
|
||||
{
|
||||
return m_typelib;
|
||||
}
|
||||
void set_typelib(const TYPELIB *typelib)
|
||||
{
|
||||
m_typelib= typelib;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Type_all_attributes: public Type_std_attributes
|
||||
{
|
||||
public:
|
||||
@ -3411,8 +3503,8 @@ public:
|
||||
virtual void set_type_maybe_null(bool maybe_null_arg)= 0;
|
||||
// Returns total number of decimal digits
|
||||
virtual decimal_digits_t decimal_precision() const= 0;
|
||||
virtual const TYPELIB *get_typelib() const= 0;
|
||||
virtual void set_typelib(const TYPELIB *typelib)= 0;
|
||||
virtual Type_extra_attributes *type_extra_attributes_addr() = 0;
|
||||
virtual const Type_extra_attributes type_extra_attributes() const= 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -22,6 +22,24 @@
|
||||
#include "sql_type_geom.h"
|
||||
#include "item_geofunc.h"
|
||||
|
||||
|
||||
class GeomTypeStr: public BinaryStringBuffer<64>
|
||||
{
|
||||
public:
|
||||
GeomTypeStr(const Type_handler &th, const Type_geom_attributes &gattr)
|
||||
{
|
||||
append_char('`');
|
||||
append(th.name().lex_cstring());
|
||||
append_char(' ');
|
||||
append(STRING_WITH_LEN("ref_system_id="));
|
||||
append_ulonglong(gattr.get_srid());
|
||||
append_char('`');
|
||||
DBUG_ASSERT(str_length < Alloced_length);
|
||||
Ptr[str_length]= '\0';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Named_type_handler<Type_handler_geometry> type_handler_geometry("geometry");
|
||||
Named_type_handler<Type_handler_point> type_handler_point("point");
|
||||
Named_type_handler<Type_handler_linestring> type_handler_linestring("linestring");
|
||||
@ -256,7 +274,7 @@ Field *Type_handler_geometry::make_conversion_table_field(MEM_ROOT *root,
|
||||
const Field_geom *fg= static_cast<const Field_geom*>(target);
|
||||
return new (root)
|
||||
Field_geom(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
|
||||
table->s, 4, fg->type_handler_geom(), fg->srid);
|
||||
table->s, 4, fg->type_handler_geom(), *fg);
|
||||
}
|
||||
|
||||
|
||||
@ -272,7 +290,8 @@ void Type_handler_geometry::
|
||||
Column_definition *def,
|
||||
const Field *field) const
|
||||
{
|
||||
def->srid= ((Field_geom*) field)->srid;
|
||||
DBUG_ASSERT(dynamic_cast<const Field_geom*>(field));
|
||||
static_cast<const Field_geom*>(field)->Type_geom_attributes::store(def);
|
||||
}
|
||||
|
||||
|
||||
@ -486,13 +505,14 @@ Field *Type_handler_geometry::make_table_field(MEM_ROOT *root,
|
||||
{
|
||||
return new (root)
|
||||
Field_geom(addr.ptr(), addr.null_ptr(), addr.null_bit(),
|
||||
Field::NONE, name, share, 4, this, 0);
|
||||
Field::NONE, name, share, 4, this,
|
||||
Type_geom_attributes(attr.type_extra_attributes()));
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_geometry::
|
||||
Item_hybrid_func_fix_attributes(THD *thd,
|
||||
const LEX_CSTRING &func_name,
|
||||
const LEX_CSTRING &op_name,
|
||||
Type_handler_hybrid_field_type *handler,
|
||||
Type_all_attributes *func,
|
||||
Item **items, uint nitems) const
|
||||
@ -503,6 +523,24 @@ bool Type_handler_geometry::
|
||||
func->decimals= 0;
|
||||
func->max_length= (uint32) UINT_MAX32;
|
||||
func->set_type_maybe_null(true);
|
||||
Type_extra_attributes *func_eattr= func->type_extra_attributes_addr();
|
||||
if (func_eattr && nitems > 0)
|
||||
{
|
||||
Type_geom_attributes gattr(items[0]->type_extra_attributes());
|
||||
for (uint32 i= 1; i < nitems; i++)
|
||||
{
|
||||
const Type_geom_attributes gattr1(items[i]->type_extra_attributes());
|
||||
if (gattr.join(gattr1))
|
||||
{
|
||||
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
|
||||
GeomTypeStr(*items[i-1]->type_handler(), gattr).ptr(),
|
||||
GeomTypeStr(*items[i]->type_handler(), gattr1).ptr(),
|
||||
op_name.str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
gattr.store(func_eattr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -660,7 +698,8 @@ Field *Type_handler_geometry::
|
||||
return new (root)
|
||||
Field_geom(rec.ptr(), rec.null_ptr(), rec.null_bit(),
|
||||
attr->unireg_check, name, share,
|
||||
attr->pack_flag_to_pack_length(), this, attr->srid);
|
||||
attr->pack_flag_to_pack_length(), this,
|
||||
Type_geom_attributes(*attr));
|
||||
}
|
||||
|
||||
|
||||
@ -708,7 +747,7 @@ Type_handler_geometry::
|
||||
cbuf[5]= (uchar) def.decimals;
|
||||
|
||||
cbuf[6]= FIELDGEOM_SRID;
|
||||
int4store(cbuf + 7, ((uint32) def.srid));
|
||||
int4store(cbuf + 7, Type_geom_attributes(def).get_srid());
|
||||
|
||||
cbuf[11]= FIELDGEOM_END;
|
||||
}
|
||||
@ -775,11 +814,13 @@ bool Type_handler_geometry::
|
||||
{
|
||||
uint gis_opt_read, gis_length, gis_decimals;
|
||||
Field_geom::storage_type st_type;
|
||||
uint32 srid= 0;
|
||||
attr->frm_unpack_basic(buffer);
|
||||
gis_opt_read= gis_field_options_read(gis_options->str,
|
||||
gis_options->length,
|
||||
&st_type, &gis_length,
|
||||
&gis_decimals, &attr->srid);
|
||||
&gis_decimals, &srid);
|
||||
Type_geom_attributes(srid).store(attr);
|
||||
gis_options->str+= gis_opt_read;
|
||||
gis_options->length-= gis_opt_read;
|
||||
return false;
|
||||
|
@ -24,6 +24,40 @@
|
||||
#include "mariadb.h"
|
||||
#include "sql_type.h"
|
||||
|
||||
|
||||
class Type_geom_attributes
|
||||
{
|
||||
protected:
|
||||
uint32 m_srid;
|
||||
public:
|
||||
Type_geom_attributes()
|
||||
:m_srid(0)
|
||||
{ }
|
||||
explicit Type_geom_attributes(const Type_extra_attributes &eattr)
|
||||
:m_srid(eattr.get_attr_uint32(0))
|
||||
{ }
|
||||
explicit Type_geom_attributes(uint32 srid)
|
||||
:m_srid(srid)
|
||||
{ }
|
||||
void store(Type_extra_attributes *to) const
|
||||
{
|
||||
to->set_attr_uint32(0, m_srid);
|
||||
}
|
||||
void set_srid(uint32 srid)
|
||||
{
|
||||
m_srid= srid;
|
||||
}
|
||||
uint32 get_srid() const
|
||||
{
|
||||
return m_srid;
|
||||
}
|
||||
bool join(const Type_geom_attributes &rhs)
|
||||
{
|
||||
return m_srid != rhs.m_srid;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef HAVE_SPATIAL
|
||||
class Type_handler_geometry: public Type_handler_string_result
|
||||
{
|
||||
@ -326,11 +360,11 @@ Type_collection_geometry_handler_by_name(const LEX_CSTRING &name);
|
||||
|
||||
#include "field.h"
|
||||
|
||||
class Field_geom :public Field_blob
|
||||
class Field_geom :public Field_blob,
|
||||
public Type_geom_attributes
|
||||
{
|
||||
const Type_handler_geometry *m_type_handler;
|
||||
public:
|
||||
uint srid;
|
||||
uint precision;
|
||||
enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1};
|
||||
enum storage_type storage;
|
||||
@ -339,11 +373,12 @@ public:
|
||||
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
|
||||
TABLE_SHARE *share, uint blob_pack_length,
|
||||
const Type_handler_geometry *gth,
|
||||
uint field_srid)
|
||||
const Type_geom_attributes &geom_attr)
|
||||
:Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
|
||||
field_name_arg, share, blob_pack_length, &my_charset_bin),
|
||||
Type_geom_attributes(geom_attr),
|
||||
m_type_handler(gth)
|
||||
{ srid= field_srid; }
|
||||
{ }
|
||||
enum_conv_type rpl_conv_type_from(const Conv_source &source,
|
||||
const Relay_log_info *rli,
|
||||
const Conv_param ¶m) const override;
|
||||
@ -363,6 +398,12 @@ public:
|
||||
{
|
||||
m_type_handler= th;
|
||||
}
|
||||
const Type_extra_attributes type_extra_attributes() const override
|
||||
{
|
||||
Type_extra_attributes eattr;
|
||||
Type_geom_attributes::store(&eattr);
|
||||
return eattr;
|
||||
}
|
||||
enum_field_types type() const override
|
||||
{
|
||||
return MYSQL_TYPE_GEOMETRY;
|
||||
@ -428,7 +469,6 @@ public:
|
||||
bool load_data_set_null(THD *thd) override;
|
||||
bool load_data_set_no_data(THD *thd, bool fixed_format) override;
|
||||
|
||||
uint get_srid() const { return srid; }
|
||||
void print_key_value(String *out, uint32 length) override
|
||||
{
|
||||
out->append(STRING_WITH_LEN("unprintable_geometry_value"));
|
||||
|
@ -6494,11 +6494,11 @@ real_type:
|
||||
|
||||
srid_option:
|
||||
/* empty */
|
||||
{ Lex->last_field->srid= 0; }
|
||||
{ Lex->last_field->set_attr_uint32(0, 0); }
|
||||
|
|
||||
REF_SYSTEM_ID_SYM '=' NUM
|
||||
{
|
||||
Lex->last_field->srid=atoi($3.str);
|
||||
Lex->last_field->set_attr_uint32(0, (uint32) atoi($3.str));
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -2734,7 +2734,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
|
||||
/* Convert pre-10.2.2 timestamps to use Field::default_value */
|
||||
name.str= fieldnames.type_names[i];
|
||||
name.length= strlen(name.str);
|
||||
attr.interval= interval_nr ? share->intervals + interval_nr - 1 : NULL;
|
||||
attr.set_typelib(interval_nr ? share->intervals + interval_nr - 1 : NULL);
|
||||
Record_addr addr(record + recpos, null_pos, null_bit_pos);
|
||||
*field_ptr= reg_field=
|
||||
attr.make_field(share, &share->mem_root, &addr, handler, &name, flags);
|
||||
@ -10573,7 +10573,7 @@ bool TR_table::check(bool error)
|
||||
}
|
||||
|
||||
Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]);
|
||||
const st_typelib *typelib= iso_level->typelib;
|
||||
const st_typelib *typelib= iso_level->typelib();
|
||||
|
||||
if (typelib->count != 4)
|
||||
goto wrong_enum;
|
||||
|
@ -644,7 +644,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
|
||||
{
|
||||
if (field->save_interval)
|
||||
{
|
||||
field->interval= field->save_interval;
|
||||
field->set_typelib(field->save_interval);
|
||||
field->save_interval= 0;
|
||||
}
|
||||
}
|
||||
@ -887,7 +887,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
|
||||
n_length+= field->field_name.length + 1;
|
||||
field->interval_id=0;
|
||||
field->save_interval= 0;
|
||||
if (field->interval)
|
||||
if (field->typelib())
|
||||
{
|
||||
uint old_int_count=int_count;
|
||||
|
||||
@ -902,9 +902,9 @@ static bool pack_header(THD *thd, uchar *forminfo,
|
||||
filled with default values it is saved in save_interval
|
||||
The HEX representation is created from this copy.
|
||||
*/
|
||||
uint count= field->interval->count;
|
||||
field->save_interval= field->interval;
|
||||
field->interval= tmpint= (TYPELIB*) thd->alloc(sizeof(TYPELIB));
|
||||
uint count= field->typelib()->count;
|
||||
field->save_interval= field->typelib();
|
||||
field->set_typelib(tmpint= (TYPELIB*) thd->alloc(sizeof(TYPELIB)));
|
||||
*tmpint= *field->save_interval;
|
||||
tmpint->type_names=
|
||||
(const char **) thd->alloc(sizeof(char*) *
|
||||
@ -913,7 +913,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
|
||||
tmpint->type_names[count]= 0;
|
||||
tmpint->type_lengths[count]= 0;
|
||||
|
||||
for (uint pos= 0; pos < field->interval->count; pos++)
|
||||
for (uint pos= 0; pos < field->typelib()->count; pos++)
|
||||
{
|
||||
char *dst;
|
||||
const char *src= field->save_interval->type_names[pos];
|
||||
@ -929,8 +929,8 @@ static bool pack_header(THD *thd, uchar *forminfo,
|
||||
field->interval_id=get_interval_id(&int_count,create_fields,field);
|
||||
if (old_int_count != int_count)
|
||||
{
|
||||
int_length+= typelib_values_packed_length(field->interval);
|
||||
int_parts+= field->interval->count + 1;
|
||||
int_length+= typelib_values_packed_length(field->typelib());
|
||||
int_parts+= field->typelib()->count + 1;
|
||||
}
|
||||
}
|
||||
if (f_maybe_null(field->pack_flag))
|
||||
@ -984,7 +984,7 @@ static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
|
||||
{
|
||||
List_iterator<Create_field> it(create_fields);
|
||||
Create_field *field;
|
||||
const TYPELIB *interval= last_field->interval;
|
||||
const TYPELIB *interval= last_field->typelib();
|
||||
|
||||
while ((field=it++) != last_field)
|
||||
{
|
||||
@ -996,11 +996,11 @@ static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
|
||||
- mbminlen>1 are written to FRM in hex-encoded format
|
||||
*/
|
||||
if (field->interval_id &&
|
||||
field->interval->count == interval->count &&
|
||||
field->typelib()->count == interval->count &&
|
||||
field->charset->mbminlen == last_field->charset->mbminlen)
|
||||
{
|
||||
const char **a,**b;
|
||||
for (a=field->interval->type_names, b=interval->type_names ;
|
||||
for (a= field->typelib()->type_names, b= interval->type_names ;
|
||||
*a && !strcmp(*a,*b);
|
||||
a++,b++) ;
|
||||
|
||||
@ -1028,7 +1028,7 @@ static size_t packed_fields_length(List<Create_field> &create_fields)
|
||||
{
|
||||
int_count= field->interval_id;
|
||||
length++;
|
||||
length+= typelib_values_packed_length(field->interval);
|
||||
length+= typelib_values_packed_length(field->typelib());
|
||||
length++;
|
||||
}
|
||||
|
||||
@ -1096,8 +1096,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
|
||||
|
||||
bzero(occ, sizeof(occ));
|
||||
|
||||
for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++)
|
||||
for (uint j = 0; j < field->interval->type_lengths[i]; j++)
|
||||
for (i=0; (val= (unsigned char*) field->typelib()->type_names[i]); i++)
|
||||
for (uint j = 0; j < field->typelib()->type_lengths[i]; j++)
|
||||
occ[(unsigned int) (val[j])]= 1;
|
||||
|
||||
if (!occ[(unsigned char)NAMES_SEP_CHAR])
|
||||
@ -1127,10 +1127,11 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
|
||||
|
||||
int_count= field->interval_id;
|
||||
*buff++= sep;
|
||||
for (int i=0; field->interval->type_names[i]; i++)
|
||||
for (int i=0; field->typelib()->type_names[i]; i++)
|
||||
{
|
||||
memcpy(buff, field->interval->type_names[i], field->interval->type_lengths[i]);
|
||||
buff+= field->interval->type_lengths[i];
|
||||
memcpy(buff, field->typelib()->type_names[i],
|
||||
field->typelib()->type_lengths[i]);
|
||||
buff+= field->typelib()->type_lengths[i];
|
||||
*buff++= sep;
|
||||
}
|
||||
*buff++= 0;
|
||||
@ -1214,8 +1215,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
|
||||
Record_addr addr(buff + field->offset + data_offset,
|
||||
null_pos + null_count / 8, null_count & 7);
|
||||
Column_definition_attributes tmp(*field);
|
||||
tmp.interval= field->save_interval ?
|
||||
field->save_interval : field->interval;
|
||||
tmp.set_typelib(field->save_interval ?
|
||||
field->save_interval : field->typelib());
|
||||
/* regfield don't have to be deleted as it's allocated on THD::mem_root */
|
||||
Field *regfield= tmp.make_field(&share, thd->mem_root, &addr,
|
||||
field->type_handler(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user