mysql_prepare_create_table() does my_qsort(sort_keys) on key
info. This sorting is indeterministic: a table is created with one
order and inplace alter may overwrite frm with another order. Since
inplace alter does nothing about key info for MyISAM/Aria storage
engines this results in discrepancy between frm and storage engine key
definitions.
The fix avoids the sorting of keys when no new keys added by ALTER
(and this is ok for MyISAM/Aria since it cannot add new keys inplace).
Notes:
mi_keydef_write()/mi_keyseg_write() are used only in mi_create(). They
should be used in ha_inplace_alter_table() as well.
Aria corruption detection is unimplemented: maria_check_definition()
is never used!
MySQL 8.0 has this bug as well as of 8.0.26.
This breaks main.long_unique in 10.4. The new result is correct and
should be applied as it just different (original) order of keys.
Long UNIQUE HASH index silently creates virtual column index, which should
be impossible for base columns featuring AUTO_INCREMENT.
Fix: add a relevant check; add new vcol type for a prettier error message.
Also fixes:
MDEV-25399 Assertion `name.length == strlen(name.str)' failed in Item_func_sp::make_send_field
Also fixes a problem that in this scenario:
SET NAMES binary;
SELECT 'some not well-formed utf8 string';
the auto-generated column name copied the binary string value directly
to the Item name, without checking utf8 well-formedness.
After this change auto-generated column names work as follows:
- Zero bytes 0x00 are copied to the name using HEX notation
- In case of "SET NAMES binary", all bytes sequences that do not make
well-formed utf8 characters are copied to the name using HEX notation.
ER_TRUNCATED_WRONG_VALUE
Part 1: Fix for DELETE without ORDER BY
Analysis: m_current_row_for_warning doesn't increment and assumes default
value which is then used by ROW_NUMBER.
Fix: Increment m_current_row_for_warning for each processed row.
CHECK violation
Analysis: When there is constraint fail we return non-zero value for
view_check_option(). So we continue the loop which doesn't increment the
counter because it increments at the end of the loop.
Fix: Increment m_current_row_for_warning() at the beginning of loop. This
will also fix similar bugs if any, about counter not incrementing
correctly because of continue.
ER_WRONG_VALUE_COUNT_ON_ROW for the 1st row
Analysis: Current row for warning does not increment for prepare phase
Fix: Increment current row for warning if number of fields in the table and
row values dont match and number of values in rows is greater than number
of fields
Analysis: When row number is passed as parameter to set_warning() it is only
used for error/warning text but m_current_row_for_warning is not updated.
Hence default value of m_current_row_for_warning is assumed.
Fix: update m_current_row_for_warning when error/warning occurs.
Analysis: Parser was missing ROW_NUMBER as syntax for SIGNAL and RESIGNAL.
Fix: Fix parser and fix how m_row_number is copied like other attributes
to avoid ROW_NUMBER from assuming default value.
Instead of
create or replace table t1 (x int)
partition by range(x) (
partition p1 values less than (10),
partition pn values less than maxvalue);
it should be possible to type in shorter form:
create or replace table t1 (x int)
partition by range(x) (
p1 values less than (10),
pn values less than maxvalue);
As above examples demonstrate, make PARTITION keyword in partition
definition optional.
Dead code cleanup:
part_info->num_parts usage was wrong and working incorrectly in
mysql_drop_partitions() because num_parts is already updated in
prep_alter_part_table(). We don't have to update part_info->partitions
because part_info is destroyed at alter_partition_lock_handling().
Cleanups:
- DBUG_EVALUATE_IF() macro replaced by shorter form DBUG_IF();
- Typo in ER_KEY_COLUMN_DOES_NOT_EXITS.
Refactorings:
- Splitted write_log_replace_delete_frm() into write_log_delete_frm()
and write_log_replace_frm();
- partition_info via DDL_LOG_STATE;
- set_part_info_exec_log_entry() removed.
DBUG_EVALUATE removed
DBUG_EVALUTATE was only added for consistency together with
DBUG_EVALUATE_IF. It is not used anywhere in the code.
DBUG_SUICIDE() fix on release build
On release DBUG_SUICIDE() was statement. It was wrong as
DBUG_SUICIDE() is used in expression context.
Assertion `!pk->has_virtual()' failed in dict_index_build_internal_clust
while creating PRIMARY key longer than possible to store in the page.
This happened because the key was wrongly deduced as Long UNIQUE supported,
however PRIMARY KEY cannot be of that type. The main reason is that
only 8 bytes are used to store the hash, see HA_HASH_FIELD_LENGTH.
This is also why HA_NOSAME flag is removed (and caused the assertion in
turn) in open_table_from_share:
if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
{
key_part_end++;
key_info->flags&= ~HA_NOSAME;
}
To make it unique, the additional check is done by
check_duplicate_long_entries call from ha_write_row, and similar one from
ha_update_row.
PRIMARY key is already forbidden, which is checked by the first test in
main.long_unique, however is_hash_field_needed was wrongly deduced to true
in mysql_prepare_create_table in this particular case.
FIX:
* Improve the check for Key::PRIMARY type
* Simplify is_hash_field_needed deduction for a more neat reading
MySQL-5.7 mysql.user tables have a last_password_changed field.
Because before MariaDB-10.4 remained oblivious to this, the act of creating
users or otherwise changing a users row left the last_password_field with 0.
Running a MariaDB-10.4 instance on this would work correctly, until mysql_upgrade
is run, when this 0 value immediately translates to password expired
state.
MySQL-5.7 relied on the password_expired enum to indicate password
expiry so we aren't going to activate password that were expired in
MySQL-5.7.
Thanks Hans Borresen for the bug report and review of the fix.
ER_WARN_DATA_OUT_OF_RANGE
Analysis: value for row_number is hard coded into push_warning_printf() to 1.
Fix: make push_warning_printf() use m_current_row_for_warning instead of 1.
The reason for crash is that natural_sort_key(release_lock('a')) would
evaluate release_lock() twice, once in Item::is_null() and another time
in Item::val_str(). Second time it returns NULL, since lock was already
released.
Fixed to prevent double evaluation.
Leading zeros added a single byte overhead per numeric string,
even when they were. Sorting leading zeros offers only for little value
(except determinism in sort). I decided to drop it for now, we can be
like ICU, which drops leading zeros, in numeric sorting,
even with IDENTICAL collation strength.
Also, disabled virtual stored columns (thus also indexes), on Serg's request
Hopefully it is temporarily, and will be reenabled soon, when everyone is
as happy with key generation algorithm as I am.
- return error from natsort_encode_numeric_key, if it would need
to allocate memory. All needed memory was preallocated much earlier.
- Add test for sort order of leading zero vs numeric strings with suffix.
- Remove second optional parameter to natural_sort_key(), and all fraction
handling.
- Rename natsort_num2str() to natsort_encode_length() to show the intention
that it encodes string *lengths*, and not encode whitespaces and what not.
Handles lengths for which log10(len) >= 10, even if they do not happen for
MariaDB Strings (where length is limited by 32bit, and log10(len) is <= 9)
- Do not let natural sort key grow past max_packet_length.
- Split Item_func_natural_sort_key::val_str() further and add
natsort_encode_numeric_string(), which contains comment on how
whitespaces are handled.
- Simplify, and speedup to_natsort_key() in common case, by removing
handling of weird charsets utf16/32, that encode numbers in several bytes.
In rare cases utf16/32 is used, we'll convert to utf8 prior to
creating keys, and back to original charset afterwards.
The numbers should be compared as numbers, while the rest should be compared
as string.
Introduce natural_sort_key() function that transforms original string
so that the lexicographic order of such keys is suitable for
natural sort.
If charset aggregation decides to convert arguments to some specific
charset and some arguments are numbers, they'll be wrapped into a
Item_func_conv_charset(), that changes result_type() to STRING_RESULT.
Fix: don't convert numbers, sformat needs their numerical value only.