Execution of the CREATE VIEW statement sent via binary protocol
where the flags of the COM_STMT_EXECUTE request a cursor to be opened
before running the statement results in an assert failure.
This assert fails since the data member thd->lex->result has not null
value pointing to an instance of the class Select_materialize.
The data member thd->lex->result is assigned a pointer to the class
Select_materialize in the function mysql_open_cursor() that invoked
in case the packet COM_STMT_EXECUTE requests a cursor to be opened.
After thd->lex->result is assigned a pointer to an instance of the
class Select_materialize the function mysql_create_view() is called
(indirectly via the function mysql_execute_statement()) and the assert
fails.
The assert
DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
!lex->param_list.elements);
was added by the commit 591c06d4b7.
Unfortunately , the condition
!lex->result
was specified incorrect. It was supposed that the thd->lex->result
is set only by parser on handling the clauses SELECT ... INTO
but indeed it is also set inside mysql_open_cursor() and
that fact was missed by the assert's condition.
So, the fix for this issue is to just remove the condition
!lex->result
from the failing assert.
Running a query using cursor could lead to a server crash on
building a temporary table used for handling the query.
For example, the following cursor
DECLARE cur1 CURSOR FOR
SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1
WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1;
declared and executed inside a stored routine could result in server
crash on creating a temporary table used for handling the ORDER BY clause.
Crash occurred on attempt to create the temporary table's fields based
on fields whose data located in a memory root that already freed.
It happens inside the function return_zero_rows() where the method
Select_materialize::send_result_set_metadata() is invoked for cursor case.
This method calls the st_select_lex_unit::get_column_types() in order to
get a list of items with types of columns for the temporary table being created.
The method st_select_lex_unit::get_column_types() returns
first_select()->join->fields
in case it is invoked for a cursor. Unfortunately, this memory has been already
deallocated bit earlier by calling
join->join_free();
inside the function return_zero_rows().
In case the query listed in the example is run in conventional way (without
using cursor) the method st_select_lex_unit::get_column_types()
returns first_select()->item_list that is not touched by invocation
of the method join->join_free() so everything is fine for that.
So, to fix the issue the resources allocated for the JOIN class should be
released after any activities with the JOIN class has been completed,
that is as the last statement before returning from the function
return_zero_rows().
This patch includes tests both for the case when a cursor is run explicitly
from within a stored routine and for the case when a cursor is opened
implicitly as prescribed by the STMT_ATTR_CURSOR_TYPE attribute of
binary protocol (the case of prepared statement).
The test was passing some uninitialized data to libmariadb.
Mostly, the MemorySanitizer wrapper of send() detected that
some bytes were uninitialized.
The test_mdev19838() is for now disabled under MemorySanitizer,
to be fixed in MDEV-26761.
Observed in 10.4 however same code in 10.2
mariadb-server-10.4/tests/mysql_client_test.c:18209:5: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation]
18209 | if (!opt_silent)
| ^~
In file included from mariadb-server-10.4/tests/mysql_client_test.c:38:
mariadb-server-10.4/tests/mysql_client_fw.c:133:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
133 | ((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0)))
| ^
mariadb-server-10.4/tests/mysql_client_test.c:18212:7: note: in expansion of macro ‘DIE_UNLESS’
18212 | DIE_UNLESS(tm[i].year == 0);
| ^~~~~~~~~~
$ /usr/bin/cc --version
cc (GCC) 11.2.1 20210728 (Red Hat 11.2.1-1)
Test cases like the following one produce different result sets if it's run
with and without th option --ps-protocol.
CREATE TABLE t1(a INT);
--enable_metadata
(SELECT MAX(a) FROM t1) UNION (SELECT MAX(a) FROM t1);
--disable_metadata
DROP TABLE t1;
Result sets differ in metadata for the query
(SELECT MAX(a) FROM t1) UNION (SELECT MAX(a) FROM t1);
The reason for different content of query metadata is that for queries
with union the items being created on JOIN preparing phase is placed into
item_list from SELECT_LEX_UNIT whereas for queries without union item_list
from SELECT_LEX is used instead.
The problem is that array binding uses net buffer to read parameters for each
execution while each execiting with RETURNING write in the same buffer.
Solution is to allocate new net buffer to avoid changing buffer we are reading
from.
One should not change the program arguments!
This change also reduces warnings from the icc compiler.
Almost all changes are just syntax changes (adding const to
'get_one_option function' declarations).
Other changes:
- Added a few cast of 'argument' from 'const char*' to 'char *'. This
was mainly in calls to 'external' functions we don't have control of.
- Ensure that all reset of 'password command line argument' are similar.
(In almost all cases it was just adding a comment and a cast)
- In mysqlbinlog.cc and mysqld.cc there was a few cases that changed
the command line argument. These places where changed to instead allocate
the option in a MEM_ROOT to avoid changing the argument. Some of this
code was changed to ensure that different programs did parsing the
same way. Added a test case for the changes in mysqlbinlog.cc
- Changed a few variables that took their value from command line options
from 'char *' to 'const char *'.
- mysqlnd from PHP < 7.3
- mysql-connector-python any version
- mysql-connector-java any version
Relaxed check about garbage at the end of the packet in case of no parameters.
Added check for array binding.
Fixed test according to the new paradigm (allow junk at the end of the packet)
In case of direct execution(stmtid=-1, mariadb_stmt_execute_direct in C
API) application is in control of how many parameters client sends to
the server. In case this number is not equal to actual query parameters
number, the server may start to interprete packet data incorrectly, e.g.
starting from the size of null bitmap. And that could cause it to crash
at some point. The commit introduces some additional COM_STMT_EXECUTE
packet sanity checks:
- checking that "types sent" byte is set, and the value is equal to 1.
if it's not direct execution, then that value is 0 or 1.
- checking that parameter type value is a valid type, and parameter
flags value is 0 or only "unsigned" bit is set
- added more checks that read does not go beyond the end of the packet
Leave debian/additions/mysqlreport as #!/usr/bin/perl
Acknowledge that `env perl` is a hack, a complete fix
needs to consider which path perl is at and insert into
these scripts.
The usefulness of these scripts is questionable.
For some reason, adding -fsanitize=undefined (cmake -DWITH_UBSAN=ON)
to the compilation flags will cause even more warnings to be emitted.
The warning was a bogus one:
tests/mysql_client_test.c:8632:22: error: '%d' directive writing between
1 and 11 bytes into a region of size 9 [-Werror=format-overflow=]
8632 | sprintf(field, "c%d int", i);
| ^~
tests/mysql_client_test.c:8632:20: note: directive argument
in the range [-2147483648, 999]
The warning does not take into account that the lower bound of the
variable actually is 0. But, we can help the compiler and use an
unsigned variable.
On FreeBSD, perl isn't in /usr/bin, its in /usr/local/bin or
elsewhere in the path.
Like storage/{maria/unittest/,}ma_test_* , we use /usr/bin/env to
find perl and run it.
When a prepared statement parameter '?' is used in a CTE that is used
multiple times, the following happens:
- The CTE definition is re-parsed multiple times.
- There are multiple Item_param objects referring to the same "?" in
the original query.
- Prepared_statement::param has a pointer to the first of them, the
others are "clones".
- When prepared statement parameter gets the value, it should be passed
over to clones with param->sync_clones() call.
This call is made in insert_params(), etc. It was not made in
insert_params_with_log().
This would cause Item_param to not have any value which would confuse
the query optimizer.
Added the missing call.