1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Merge branch '10.6' into 10.11

This commit is contained in:
Oleksandr Byelkin
2026-08-19 08:13:54 +02:00
157 changed files with 2638 additions and 890 deletions

View File

@@ -207,7 +207,7 @@ static char TMPDIR[FN_REFLEN];
static char global_subst_from[200];
static char global_subst_to[200];
static char *global_subst= NULL;
static char *read_command_buf= NULL;
static char *read_command_buf= NULL, *read_command_buf_end;
static MEM_ROOT require_file_root;
static const my_bool my_true= 1;
static const my_bool my_false= 0;
@@ -3866,9 +3866,24 @@ end:
#endif
if (error)
{
uint status= WEXITSTATUS(error);
uint status;
int i;
#ifdef _WIN32
status= WEXITSTATUS(error);
#else
/* WEXITSTATUS() is only valid for a normal exit; a process killed by an
uncaught signal must be translated using the shell's 128+signal
convention, or the real error is silently lost as status 0. */
if (WIFEXITED(error))
status= WEXITSTATUS(error);
else if (WIFSIGNALED(error))
status= 128 + WTERMSIG(error);
else
status= error;
#endif
if (command->abort_on_error)
{
report_or_die("exec of '%s' failed, error: %d, status: %d, errno: %d\n"
@@ -5602,7 +5617,7 @@ static void primary(Expression_value *result, const char **s)
enum func_type func_type= get_expr_function_type(start,
end - start);
if (func_type == FUNC_UNKNOWN)
die("Syntax error: Unknown function");
die("Syntax error: Unknown function '%.*s'", (int) (end - start), start);
*s= end + 1; // skip '('
handle_expr_function_call(func_type, result, s);
@@ -6049,47 +6064,48 @@ static void expr(Expression_value *result, const char **s)
static struct {
const char *name;
size_t length;
enum func_type type;
} function_table[]= {
// Numeric functions
{"abs", FUNC_ABS},
{"bin", FUNC_BIN},
{"conv", FUNC_CONV},
{"hex", FUNC_HEX},
{"oct", FUNC_OCT},
{STRING_WITH_LEN("abs"), FUNC_ABS},
{STRING_WITH_LEN("bin"), FUNC_BIN},
{STRING_WITH_LEN("conv"), FUNC_CONV},
{STRING_WITH_LEN("hex"), FUNC_HEX},
{STRING_WITH_LEN("oct"), FUNC_OCT},
// String functions
{"concat", FUNC_CONCAT},
{"concat_ws", FUNC_CONCAT_WS},
{"greatest", FUNC_GREATEST},
{"insert", FUNC_INSERT},
{"instr", FUNC_INSTR},
{"lcase", FUNC_LOWER},
{"least", FUNC_LEAST},
{"length", FUNC_LENGTH},
{"locate", FUNC_LOCATE},
{"lower", FUNC_LOWER},
{"lpad", FUNC_LPAD},
{"ltrim", FUNC_LTRIM},
{"repeat", FUNC_REPEAT},
{"replace", FUNC_REPLACE},
{"reverse", FUNC_REVERSE},
{"rpad", FUNC_RPAD},
{"rtrim", FUNC_RTRIM},
{"substr", FUNC_SUBSTR},
{"substring", FUNC_SUBSTR},
{"substring_index", FUNC_SUBSTR_IDX},
{"trim", FUNC_TRIM},
{"ucase", FUNC_UPPER},
{"upper", FUNC_UPPER},
{STRING_WITH_LEN("concat"), FUNC_CONCAT},
{STRING_WITH_LEN("concat_ws"), FUNC_CONCAT_WS},
{STRING_WITH_LEN("greatest"), FUNC_GREATEST},
{STRING_WITH_LEN("insert"), FUNC_INSERT},
{STRING_WITH_LEN("instr"), FUNC_INSTR},
{STRING_WITH_LEN("lcase"), FUNC_LOWER},
{STRING_WITH_LEN("least"), FUNC_LEAST},
{STRING_WITH_LEN("length"), FUNC_LENGTH},
{STRING_WITH_LEN("locate"), FUNC_LOCATE},
{STRING_WITH_LEN("lower"), FUNC_LOWER},
{STRING_WITH_LEN("lpad"), FUNC_LPAD},
{STRING_WITH_LEN("ltrim"), FUNC_LTRIM},
{STRING_WITH_LEN("repeat"), FUNC_REPEAT},
{STRING_WITH_LEN("replace"), FUNC_REPLACE},
{STRING_WITH_LEN("reverse"), FUNC_REVERSE},
{STRING_WITH_LEN("rpad"), FUNC_RPAD},
{STRING_WITH_LEN("rtrim"), FUNC_RTRIM},
{STRING_WITH_LEN("substr"), FUNC_SUBSTR},
{STRING_WITH_LEN("substring"), FUNC_SUBSTR},
{STRING_WITH_LEN("substring_index"), FUNC_SUBSTR_IDX},
{STRING_WITH_LEN("trim"), FUNC_TRIM},
{STRING_WITH_LEN("ucase"), FUNC_UPPER},
{STRING_WITH_LEN("upper"), FUNC_UPPER},
// Regexp functions
{"regexp_instr", FUNC_REGEXP_INSTR},
{"regexp_replace", FUNC_REGEXP_REPLACE},
{"regexp_substr", FUNC_REGEXP_SUBSTR},
{STRING_WITH_LEN("regexp_instr"), FUNC_REGEXP_INSTR},
{STRING_WITH_LEN("regexp_replace"), FUNC_REGEXP_REPLACE},
{STRING_WITH_LEN("regexp_substr"), FUNC_REGEXP_SUBSTR},
// Null functions
{"coalesce", FUNC_COALESCE},
{"ifnull", FUNC_IFNULL},
{"nullif", FUNC_NULLIF},
{NULL, FUNC_UNKNOWN}
{STRING_WITH_LEN("coalesce"), FUNC_COALESCE},
{STRING_WITH_LEN("ifnull"), FUNC_IFNULL},
{STRING_WITH_LEN("nullif"), FUNC_NULLIF},
{NULL, 0, FUNC_UNKNOWN}
};
@@ -6329,9 +6345,9 @@ void func_oct(Expression_value args[], int count, Expression_value *result)
@param[out] result Expression_value to store result
@details
Converts a number to hexadecimal representation.
HEX(N) returns a string representation of the hexadecimal value of N.
This is equivalent to CONV(N, 10, 16).
HEX(str) returns a hexadecimal representation of the bytes of str.
@note Dies if argument count != 1
*/
@@ -6341,7 +6357,15 @@ void func_hex(Expression_value args[], int count, Expression_value *result)
if (count != 1)
die("hex() expects 1 argument, got %d", count);
convert_base_helper(args[0].to_string(), 10, 16, result);
if (args[0].is_numeric)
convert_base_helper(args[0].to_string(), 10, 16, result);
else
{
My_string str= args[0].to_string();
My_string hex_str;
hex_str.set_hex(str.ptr(), str.length());
result->set_string(hex_str.ptr(), hex_str.length());
}
}
@@ -7605,7 +7629,8 @@ enum func_type get_expr_function_type(const char *name, size_t len)
{
for (int i= 0; function_table[i].name; ++i)
{
if (!strncasecmp(function_table[i].name, name, len))
if (function_table[i].length == len &&
!strncasecmp(function_table[i].name, name, len))
return function_table[i].type;
}
return FUNC_UNKNOWN;
@@ -9687,7 +9712,7 @@ int read_line()
*p= 0;
DBUG_PRINT("exit", ("Found delimiter '%s' at line %d",
delimiter, cur_file->lineno));
DBUG_RETURN(0);
goto ret;
}
else if ((c == '{' &&
(!my_strnncoll_simple(charset_info, (const uchar*) "while", 5,
@@ -9700,7 +9725,7 @@ int read_line()
*p= 0;
DBUG_PRINT("exit", ("Found '{' indicating start of block at line %d",
cur_file->lineno));
DBUG_RETURN(0);
goto ret;
}
else if (c == '\'' || c == '"' || c == '`')
{
@@ -9734,7 +9759,7 @@ int read_line()
*p= 0;
DBUG_PRINT("exit", ("Found newline in comment at line: %d",
cur_file->lineno));
DBUG_RETURN(0);
goto ret;
}
break;
@@ -9754,7 +9779,7 @@ int read_line()
DBUG_PRINT("info", ("Found two new lines in a row"));
*p++= c;
*p= 0;
DBUG_RETURN(0);
goto ret;
}
/* Query hasn't started yet */
@@ -9771,7 +9796,7 @@ int read_line()
*p= 0;
DBUG_PRINT("exit", ("Found delimiter '%s' at line: %d",
delimiter, cur_file->lineno));
DBUG_RETURN(0);
goto ret;
}
else if (c == '}')
{
@@ -9780,7 +9805,7 @@ int read_line()
*p= 0;
DBUG_PRINT("exit", ("Found '}' in beginning of a line at line: %d",
cur_file->lineno));
DBUG_RETURN(0);
goto ret;
}
else if (c == '\'' || c == '"' || c == '`')
{
@@ -9839,6 +9864,8 @@ int read_line()
}
}
}
ret:
read_command_buf_end= p;
DBUG_RETURN(0);
}
@@ -9854,14 +9881,13 @@ int read_line()
*/
void convert_to_format_v1(char* query)
void convert_to_format_v1(char* query, char **end)
{
int last_c_was_quote= 0;
char *p= query, *to= query;
char *end= strend(query);
char last_c;
while (p <= end)
while (p <= *end)
{
if (*p == '\n' && !last_c_was_quote)
{
@@ -9892,6 +9918,7 @@ void convert_to_format_v1(char* query)
last_c_was_quote= 0;
}
}
*end= to;
}
@@ -10008,9 +10035,9 @@ int read_command(struct st_command** command_ptr)
}
if (opt_result_format_version == 1)
convert_to_format_v1(read_command_buf);
convert_to_format_v1(read_command_buf, &read_command_buf_end);
char *p= read_command_buf;
char *p= read_command_buf, *end= read_command_buf_end;
DBUG_PRINT("info", ("query: '%s'", read_command_buf));
if (*p == '#')
{
@@ -10027,29 +10054,32 @@ int read_command(struct st_command** command_ptr)
}
/* Skip leading spaces */
while (*p && my_isspace(charset_info, *p))
while (p < end && my_isspace(charset_info, *p))
p++;
if (!(command->query_buf= command->query= my_strdup(PSI_NOT_INSTRUMENTED, p, MYF(MY_WME))))
if (!(command->query_buf= command->query=
(char*)my_memdup(PSI_NOT_INSTRUMENTED, p, end - p, MYF(MY_WME))))
die("Out of memory");
command->query_len= (int)(end - p - 1);
command->end= command->query + command->query_len;
p= command->query;
end= command->end;
/*
Calculate first word length(the command), terminated
by 'space' , '(' or 'delimiter' */
p= command->query;
while (*p && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p))
by 'space' , '(' or 'delimiter'
*/
while (p < end && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p))
p++;
command->first_word_len= (uint) (p - command->query);
DBUG_PRINT("info", ("first_word: %.*s",
command->first_word_len, command->query));
/* Skip spaces between command and first argument */
while (*p && my_isspace(charset_info, *p))
while (p < end && my_isspace(charset_info, *p))
p++;
command->first_argument= p;
command->end= strend(command->query);
command->query_len= (int)(command->end - command->query);
parser.read_lines++;
DBUG_RETURN(0);
}
@@ -12351,7 +12381,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags)
else
{
query = command->query;
query_len = strlen(query);
query_len = command->query_len;
}
/*
@@ -12741,6 +12771,7 @@ void get_command_type(struct st_command* command)
if (type == Q_QUERY)
{
/* Skip the "query" part */
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
}
}
@@ -13312,7 +13343,8 @@ int main(int argc, char **argv)
if (command->query == command->query_buf)
{
/* Skip the first part of command, i.e query_xxx */
command->query= command->first_argument;
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
command->first_word_len= 0;
}
/* fall through */
@@ -13374,7 +13406,10 @@ int main(int argc, char **argv)
/* Remove "send" if this is first iteration */
if (command->query == command->query_buf)
{
command->query_len-= (int)(command->first_argument - command->query);
command->query= command->first_argument;
}
/*
run_query() can execute a query partially, depending on the flags.

View File

@@ -248,7 +248,7 @@ IF(WITH_WSREP)
SETA(CPACK_RPM_server_PACKAGE_REQUIRES
"galera-4" "rsync" "grep" "gawk" "iproute"
"coreutils" "findutils" "tar")
SETA(CPACK_RPM_server_PACKAGE_RECOMMENDS "lsof" "socat" "pv")
SETA(CPACK_RPM_server_PACKAGE_RECOMMENDS "lsof" "socat" "pv" "stunnel")
SETA(CPACK_RPM_test_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES}" "socat")
ENDIF()

View File

@@ -64,6 +64,9 @@
#
# - INSTALL_UNIX_ADDRDIR (path to mysql.sock)
#
# - INSTALL_RUNDIR (runtime state files that mariadbd must not write,
# e.g. what systemd reads as EnvironmentFile)
#
IF(NOT INSTALL_LAYOUT)
IF(DEB)
@@ -163,7 +166,7 @@ SET(INSTALL_UNIX_ADDRDIR_RPM "${INSTALL_MYSQLDATADIR_RPM}/mysql.sock"
SET(INSTALL_SYSTEMD_UNITDIR_RPM "/usr/lib/systemd/system")
SET(INSTALL_SYSTEMD_SYSUSERSDIR_RPM "/usr/lib/sysusers.d")
SET(INSTALL_SYSTEMD_TMPFILESDIR_RPM "/usr/lib/tmpfiles.d")
SET(INSTALL_RUNDATADIR_RPM "/run/mariadb")
SET(INSTALL_RUNDIR_RPM "/run")
SET(INSTALL_PAMDIR_RPM "/${INSTALL_LIBDIR_RPM}/security")
SET(INSTALL_PAMDATADIR_RPM "/etc/security")
@@ -194,8 +197,8 @@ SET(INSTALL_SUPPORTFILESDIR_DEB "share/mysql")
#
SET(INSTALL_MYSQLDATADIR_DEB "/var/lib/mysql")
SET(INSTALL_RUNDATADIR_DEB "/run/mysqld")
SET(INSTALL_UNIX_ADDRDIR_DEB "${INSTALL_RUNDATADIR_DEB}/mysqld.sock")
SET(INSTALL_RUNDIR_DEB "/run")
SET(INSTALL_UNIX_ADDRDIR_DEB "${INSTALL_RUNDIR_DEB}/mysqld/mysqld.sock")
SET(INSTALL_SYSTEMD_UNITDIR_DEB "/lib/systemd/system")
SET(INSTALL_SYSTEMD_SYSUSERSDIR_DEB "/usr/lib/sysusers.d")
SET(INSTALL_SYSTEMD_TMPFILESDIR_DEB "/usr/lib/tmpfiles.d")
@@ -259,7 +262,10 @@ IF(NOT MYSQL_UNIX_ADDR)
SET(MYSQL_UNIX_ADDR ${INSTALL_UNIX_ADDRDIR})
ENDIF()
IF(NOT INSTALL_RUNDATADIR)
IF(NOT INSTALL_RUNDIR)
# No /run in a tarball installation - fall back to the socket directory.
# Note that mariadbd can write there, so the isolation that /run gives to
# packages (see EnvironmentFile= in mariadb.service) is not achieved.
get_filename_component(MYSQL_UNIX_DIR ${MYSQL_UNIX_ADDR} DIRECTORY)
SET(INSTALL_RUNDATADIR "${MYSQL_UNIX_DIR}" CACHE FILEPATH "Rundata installation directory" ${FORCE})
SET(INSTALL_RUNDIR "${MYSQL_UNIX_DIR}" CACHE FILEPATH "Runtime state directory" ${FORCE})
ENDIF()

3
debian/control vendored
View File

@@ -676,7 +676,8 @@ Architecture: any
Suggests: mariadb-test,
netcat-openbsd
Recommends: libhtml-template-perl,
pv
pv,
stunnel
Pre-Depends: adduser (>= 3.40),
debconf,
mariadb-common (>= ${source:Version})

View File

@@ -321,4 +321,13 @@ drop table t1;
#
# End of 10.5 tests
#
#
# MDEV-31808 Server crash upon altering table with NEXTVAL for default under exclusive lock
#
CREATE SEQUENCE s ENGINE=Aria;
CREATE TABLE t (a INT DEFAULT(NEXTVAL(s)), b INT);
ALTER TABLE t FORCE, ALGORITHM=COPY, LOCK=EXCLUSIVE;
DROP TABLE t;
DROP SEQUENCE s;
# End of 10.6 tests
set @@default_storage_engine= @save_default_engine;

View File

@@ -260,4 +260,18 @@ drop table t1;
--echo # End of 10.5 tests
--echo #
--echo #
--echo # MDEV-31808 Server crash upon altering table with NEXTVAL for default under exclusive lock
--echo #
CREATE SEQUENCE s ENGINE=Aria;
CREATE TABLE t (a INT DEFAULT(NEXTVAL(s)), b INT);
ALTER TABLE t FORCE, ALGORITHM=COPY, LOCK=EXCLUSIVE;
# Cleanup
DROP TABLE t;
DROP SEQUENCE s;
--echo # End of 10.6 tests
set @@default_storage_engine= @save_default_engine;

View File

@@ -110,6 +110,8 @@ Warning 4076 Incorrect GeoJSON format - empty 'coordinates' array.
SELECT ST_GEOMFROMGEOJSON("{ \"type\": \"Feature\", \"geometry\": [10, 20] }") as exp;
exp
NULL
Warnings:
Warning 4048 Incorrect GeoJSON format specified for st_geomfromgeojson function.
SELECT ST_ASTEXT (ST_GEOMFROMGEOJSON ('{ "type": "GEOMETRYCOLLECTION", "coordinates": [102.0, 0.0]}')) as exp;
exp
NULL
@@ -135,7 +137,126 @@ repeat(']}', 2000)
exp
NULL
Warnings:
Warning 4040 Limit of 32 on JSON nested structures depth is reached in argument 1 to function 'st_geomfromgeojson' at position 473
Warning 4040 Limit of 32 on JSON nested structures depth is reached in argument 1 to function 'st_geomfromgeojson' at position 688
# Boundary condition: foreign-member nesting at depth 32
SET @geom := '{"type":"Point","coordinates":[1,2]}';
SET @doc := CONCAT(
REPEAT('{"x":', 29),
@geom,
REPEAT('}', 29)
);
SELECT ST_ASTEXT( ST_GeomFromGeoJSON(@doc) ) AS expr;
expr
NULL
Warnings:
Warning 4048 Incorrect GeoJSON format specified for st_geomfromgeojson function.
# Foreign-member nesting exceeds depth 32
SET @geom := '{"type":"Point","coordinates":[1,2]}';
SET @doc := CONCAT(
REPEAT('{"x":', 30),
@geom,
REPEAT('}', 30)
);
SELECT ST_GeomFromGeoJSON(@doc) AS expr;
expr
NULL
Warnings:
Warning 4040 Limit of 32 on JSON nested structures depth is reached in argument 1 to function 'st_geomfromgeojson' at position 181
# Deep foreign member attached to otherwise valid GeoJSON
SET @foreign := CONCAT(
REPEAT('{"x":', 28),
'{"leaf":1}',
REPEAT('}', 28)
);
SET @doc := CONCAT(
'{"type":"Point","coordinates":[10,20],"foreign":',
@foreign,
'}'
);
SELECT ST_ASTEXT(ST_GeomFromGeoJSON(@doc)) AS expr;
expr
POINT(10 20)
# Foreign member exceeds depth limit
SET @foreign := CONCAT(
REPEAT('{"x":', 29),
'{"leaf":1}',
REPEAT('}', 29)
);
SET @doc := CONCAT(
'{"type":"Point","coordinates":[10,20],"foreign":',
@foreign,
'}'
);
SELECT ST_ASTEXT(ST_GeomFromGeoJSON(@doc)) AS expr;
expr
POINT(10 20)
# GeometryCollection containing 33 valid GeoJSON Point objects
SET @point := '{"type":"Point","coordinates":[1,2]}';
SET @geoms := CONCAT(
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point
);
SET @doc := CONCAT(
'{"type":"GeometryCollection","geometries":[',
@geoms,
']}'
);
SELECT ST_AsText(ST_GeomFromGeoJSON(@doc)) AS expr;
expr
GEOMETRYCOLLECTION(POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2),POINT(1 2))
#
# MDEV-39981: ST_GEOMFROMGEOJSON returns wrong result with reversed key order
#
# GeometryCollection: type before geometries (baseline)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"type":"GeometryCollection","geometries":[]}')) as expr;
expr
GEOMETRYCOLLECTION EMPTY
# GeometryCollection: geometries before type (empty)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[],"type":"GeometryCollection"}')) as expr;
expr
GEOMETRYCOLLECTION EMPTY
# GeometryCollection: geometries before type (non-empty)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"type":"Point","coordinates":[0,1]}],"type":"GeometryCollection"}')) as expr;
expr
GEOMETRYCOLLECTION(POINT(0 1))
# GeometryCollection: multiple geometries before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"type":"Point","coordinates":[1,2]},{"type":"Point","coordinates":[3,4]}],"type":"GeometryCollection"}')) as expr;
expr
GEOMETRYCOLLECTION(POINT(1 2),POINT(3 4))
# Point: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[0,1],"type":"Point"}')) as expr;
expr
POINT(0 1)
# LineString: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[[0,0],[1,1],[2,2]],"type":"LineString"}')) as expr;
expr
LINESTRING(0 0,1 1,2 2)
# Polygon: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[[[0,0],[1,0],[1,1],[0,1],[0,0]]],"type":"Polygon"}')) as expr;
expr
POLYGON((0 0,1 0,1 1,0 1,0 0))
# FeatureCollection: features before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"features":[{"geometry":{"type":"Point","coordinates":[5,6]},"type":"Feature","properties":{}}],"type":"FeatureCollection"}')) as expr;
expr
GEOMETRYCOLLECTION(POINT(5 6))
# Feature: geometry before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometry":{"coordinates":[7,8],"type":"Point"},"type":"Feature","properties":{}}')) as expr;
expr
POINT(7 8)
# Unknown keys interspersed (should be ignored)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"foo":"bar","geometries":[],"type":"GeometryCollection","extra":123}')) as expr;
expr
GEOMETRYCOLLECTION EMPTY
# Deeply nested geometries before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"geometries":[{"type":"Point","coordinates":[9,10]}],"type":"GeometryCollection"}],"type":"GeometryCollection"}')) as expr;
expr
GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POINT(9 10)))
# End of 10.6 tests
#
# MDEV-34079: ST_AsGeoJSON returns incorrect value for empty geometry

View File

@@ -70,6 +70,120 @@ SELECT ST_GeomFromGeoJSON(
repeat(']}', 2000)
)) as exp;
--echo # Boundary condition: foreign-member nesting at depth 32
SET @geom := '{"type":"Point","coordinates":[1,2]}';
SET @doc := CONCAT(
REPEAT('{"x":', 29),
@geom,
REPEAT('}', 29)
);
SELECT ST_ASTEXT( ST_GeomFromGeoJSON(@doc) ) AS expr;
--echo # Foreign-member nesting exceeds depth 32
SET @geom := '{"type":"Point","coordinates":[1,2]}';
SET @doc := CONCAT(
REPEAT('{"x":', 30),
@geom,
REPEAT('}', 30)
);
SELECT ST_GeomFromGeoJSON(@doc) AS expr;
--echo # Deep foreign member attached to otherwise valid GeoJSON
SET @foreign := CONCAT(
REPEAT('{"x":', 28),
'{"leaf":1}',
REPEAT('}', 28)
);
SET @doc := CONCAT(
'{"type":"Point","coordinates":[10,20],"foreign":',
@foreign,
'}'
);
SELECT ST_ASTEXT(ST_GeomFromGeoJSON(@doc)) AS expr;
--echo # Foreign member exceeds depth limit
SET @foreign := CONCAT(
REPEAT('{"x":', 29),
'{"leaf":1}',
REPEAT('}', 29)
);
SET @doc := CONCAT(
'{"type":"Point","coordinates":[10,20],"foreign":',
@foreign,
'}'
);
SELECT ST_ASTEXT(ST_GeomFromGeoJSON(@doc)) AS expr;
--echo # GeometryCollection containing 33 valid GeoJSON Point objects
SET @point := '{"type":"Point","coordinates":[1,2]}';
SET @geoms := CONCAT(
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point, ',', @point, ',', @point, ',',
@point, ',', @point, ',', @point
);
SET @doc := CONCAT(
'{"type":"GeometryCollection","geometries":[',
@geoms,
']}'
);
SELECT ST_AsText(ST_GeomFromGeoJSON(@doc)) AS expr;
--echo #
--echo # MDEV-39981: ST_GEOMFROMGEOJSON returns wrong result with reversed key order
--echo #
--echo # GeometryCollection: type before geometries (baseline)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"type":"GeometryCollection","geometries":[]}')) as expr;
--echo # GeometryCollection: geometries before type (empty)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[],"type":"GeometryCollection"}')) as expr;
--echo # GeometryCollection: geometries before type (non-empty)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"type":"Point","coordinates":[0,1]}],"type":"GeometryCollection"}')) as expr;
--echo # GeometryCollection: multiple geometries before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"type":"Point","coordinates":[1,2]},{"type":"Point","coordinates":[3,4]}],"type":"GeometryCollection"}')) as expr;
--echo # Point: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[0,1],"type":"Point"}')) as expr;
--echo # LineString: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[[0,0],[1,1],[2,2]],"type":"LineString"}')) as expr;
--echo # Polygon: coordinates before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"coordinates":[[[0,0],[1,0],[1,1],[0,1],[0,0]]],"type":"Polygon"}')) as expr;
--echo # FeatureCollection: features before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"features":[{"geometry":{"type":"Point","coordinates":[5,6]},"type":"Feature","properties":{}}],"type":"FeatureCollection"}')) as expr;
--echo # Feature: geometry before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometry":{"coordinates":[7,8],"type":"Point"},"type":"Feature","properties":{}}')) as expr;
--echo # Unknown keys interspersed (should be ignored)
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"foo":"bar","geometries":[],"type":"GeometryCollection","extra":123}')) as expr;
--echo # Deeply nested geometries before type
SELECT ST_ASTEXT(ST_GEOMFROMGEOJSON('{"geometries":[{"geometries":[{"type":"Point","coordinates":[9,10]}],"type":"GeometryCollection"}],"type":"GeometryCollection"}')) as expr;
--echo # End of 10.6 tests
--echo #

View File

@@ -494,6 +494,34 @@ CREATE USER for u2@localhost
CREATE USER `u2`@`localhost` IDENTIFIED BY PASSWORD '*975B2CD4FF9AE554FE8AD33168FBFC326D2021DD'
drop user u1@localhost;
drop user u2@localhost;
#
# MDEV-40541 role vs user@localhost acl_cache key confusion
#
create database db;
create table db.t1(a varchar(40));
insert into db.t1 values ('role-cache-collision-row');
create role r1;
create user r1@localhost identified by 'r1pass';
create user u1@'127.0.0.1' identified by 'u1pass';
grant select on db.* to r1@localhost;
grant r1 to u1@'127.0.0.1';
connect r1,localhost,r1,r1pass;
select a as precache from db.t1;
precache
role-cache-collision-row
connect u1,127.0.0.1,u1,u1pass;
set role r1;
show grants;
Grants for u1@127.0.0.1
GRANT `r1` TO `u1`@`127.0.0.1`
GRANT USAGE ON *.* TO `u1`@`127.0.0.1` IDENTIFIED BY PASSWORD '*4C5C0A2C80D7567C7CFBDFC09409901AADC4B390'
GRANT USAGE ON *.* TO `r1`
select a from db.t1;
ERROR 42000: SELECT command denied to user 'u1'@'localhost' for table `db`.`t1`
connection default;
drop database db;
drop role r1;
drop user r1@localhost, u1@'127.0.0.1';
# End of 10.6 tests
#
# MDEV-37256: The permission check of LOAD INDEX INTO CACHE and CACHE INDEX is broken

View File

@@ -455,6 +455,35 @@ show create user u2@localhost;
drop user u1@localhost;
drop user u2@localhost;
--echo #
--echo # MDEV-40541 role vs user@localhost acl_cache key confusion
--echo #
create database db;
create table db.t1(a varchar(40));
insert into db.t1 values ('role-cache-collision-row');
create role r1;
create user r1@localhost identified by 'r1pass';
create user u1@'127.0.0.1' identified by 'u1pass';
grant select on db.* to r1@localhost;
grant r1 to u1@'127.0.0.1';
#flush privileges;
connect r1,localhost,r1,r1pass;
select a as precache from db.t1;
connect u1,127.0.0.1,u1,u1pass;
set role r1;
show grants;
--error ER_TABLEACCESS_DENIED_ERROR
select a from db.t1;
connection default;
drop database db;
drop role r1;
drop user r1@localhost, u1@'127.0.0.1';
--echo # End of 10.6 tests
--echo #

View File

@@ -1356,3 +1356,69 @@ a b
1 2
drop tables t1, t2, t3;
set sql_mode= default;
#
# MDEV-38722 server crash hp_rec_key_cmp
#
# original reproducer (used to crash), returns a single row
SELECT 1 INTERSECT SELECT 1 UNION ALL SELECT 1 EXCEPT ALL SELECT 1;
1
1
CREATE TABLE t1 (i INT);
INSERT INTO t1 VALUES (1),(1),(2),(3);
CREATE TABLE t2 (i INT);
INSERT INTO t2 VALUES (1),(2),(2);
CREATE TABLE t3 (i INT);
INSERT INTO t3 VALUES (1),(3);
SELECT i FROM t1 INTERSECT SELECT i FROM t3
UNION ALL SELECT i FROM t2 EXCEPT ALL SELECT i FROM t3 ORDER BY i;
i
1
2
2
(((SELECT i FROM t1) INTERSECT (SELECT i FROM t3)) UNION ALL (SELECT i FROM t2))
EXCEPT ALL (SELECT i FROM t3) ORDER BY i;
i
1
2
2
# trailing UNION ALL after the EXCEPT ALL
SELECT i FROM t1 INTERSECT SELECT i FROM t2 UNION ALL SELECT i FROM t2
EXCEPT ALL SELECT i FROM t3 UNION ALL SELECT i FROM t1 ORDER BY i;
i
1
1
1
2
2
2
2
3
((((SELECT i FROM t1) INTERSECT (SELECT i FROM t2)) UNION ALL (SELECT i FROM t2))
EXCEPT ALL (SELECT i FROM t3)) UNION ALL (SELECT i FROM t1) ORDER BY i;
i
1
1
1
2
2
2
2
3
# a leading INTERSECT ALL keeps the operation extended while the last
# INTERSECT DISTINCT node is the index-release point
SELECT i FROM t1 INTERSECT ALL SELECT i FROM t2 INTERSECT SELECT i FROM t3
UNION ALL SELECT i FROM t2 ORDER BY i;
i
1
1
2
2
(((SELECT i FROM t1) INTERSECT ALL (SELECT i FROM t2)) INTERSECT (SELECT i FROM t3))
UNION ALL (SELECT i FROM t2) ORDER BY i;
i
1
1
2
2
DROP TABLE t1,t2,t3;
# End of 10.6 tests

View File

@@ -497,3 +497,34 @@ select * from t1 except all select * from t2 intersect all select * from t1 unio
select * from t1 except all select * from t2 intersect all select * from t1 union all select * from t2 order by a desc limit 3;
drop tables t1, t2, t3;
set sql_mode= default;
--echo #
--echo # MDEV-38722 server crash hp_rec_key_cmp
--echo #
--echo # original reproducer (used to crash), returns a single row
SELECT 1 INTERSECT SELECT 1 UNION ALL SELECT 1 EXCEPT ALL SELECT 1;
CREATE TABLE t1 (i INT);
INSERT INTO t1 VALUES (1),(1),(2),(3);
CREATE TABLE t2 (i INT);
INSERT INTO t2 VALUES (1),(2),(2);
CREATE TABLE t3 (i INT);
INSERT INTO t3 VALUES (1),(3);
SELECT i FROM t1 INTERSECT SELECT i FROM t3
UNION ALL SELECT i FROM t2 EXCEPT ALL SELECT i FROM t3 ORDER BY i;
(((SELECT i FROM t1) INTERSECT (SELECT i FROM t3)) UNION ALL (SELECT i FROM t2))
EXCEPT ALL (SELECT i FROM t3) ORDER BY i;
--echo # trailing UNION ALL after the EXCEPT ALL
SELECT i FROM t1 INTERSECT SELECT i FROM t2 UNION ALL SELECT i FROM t2
EXCEPT ALL SELECT i FROM t3 UNION ALL SELECT i FROM t1 ORDER BY i;
((((SELECT i FROM t1) INTERSECT (SELECT i FROM t2)) UNION ALL (SELECT i FROM t2))
EXCEPT ALL (SELECT i FROM t3)) UNION ALL (SELECT i FROM t1) ORDER BY i;
--echo # a leading INTERSECT ALL keeps the operation extended while the last
--echo # INTERSECT DISTINCT node is the index-release point
SELECT i FROM t1 INTERSECT ALL SELECT i FROM t2 INTERSECT SELECT i FROM t3
UNION ALL SELECT i FROM t2 ORDER BY i;
(((SELECT i FROM t1) INTERSECT ALL (SELECT i FROM t2)) INTERSECT (SELECT i FROM t3))
UNION ALL (SELECT i FROM t2) ORDER BY i;
DROP TABLE t1,t2,t3;
--echo # End of 10.6 tests

View File

@@ -103,4 +103,16 @@ drop table t3|
# MDEV-40409 ST_GeomFromText stack overrun on deeply nested GeometryCollection
#
SELECT ST_GeomFromText(CONCAT(REPEAT('GEOMETRYCOLLECTION(',5000),'POINT(1 1)',REPEAT(')',5000)));
#
# MDEV-40540 ST_GeomFromWKB stack overrun on deeply nested GeometryCollection
#
set @a=concat(0x0107000000, repeat(0x010000000107000000, 120000), 0x00000000);
SELECT ST_GeomFromWKB(@a);
#
# MDEV-39750 ExtractValue does not control recursion depth.
#
SELECT ExtractValue('<a/>', REPEAT('(', 100000));
ERROR HY000: Thread stack overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed. Consider increasing the thread_stack system variable.
SELECT ExtractValue('<a/>', REPEAT('/n', 100000));
ERROR HY000: Thread stack overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed. Consider increasing the thread_stack system variable.
# End of 10.6 tests

View File

@@ -147,4 +147,24 @@ delimiter ;|
SELECT ST_GeomFromText(CONCAT(REPEAT('GEOMETRYCOLLECTION(',5000),'POINT(1 1)',REPEAT(')',5000)));
--enable_result_log
--echo #
--echo # MDEV-40540 ST_GeomFromWKB stack overrun on deeply nested GeometryCollection
--echo #
set @a=concat(0x0107000000, repeat(0x010000000107000000, 120000), 0x00000000);
--disable_result_log
--error ER_STACK_OVERRUN_NEED_MORE
SELECT ST_GeomFromWKB(@a);
--enable_result_log
--echo #
--echo # MDEV-39750 ExtractValue does not control recursion depth.
--echo #
--replace_regex /overrun: [0-9]* bytes used of a [0-9]* byte stack, and [0-9]* bytes needed/overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed/
--error ER_STACK_OVERRUN_NEED_MORE
SELECT ExtractValue('<a/>', REPEAT('(', 100000));
--replace_regex /overrun: [0-9]* bytes used of a [0-9]* byte stack, and [0-9]* bytes needed/overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed/
--error ER_STACK_OVERRUN_NEED_MORE
SELECT ExtractValue('<a/>', REPEAT('/n', 100000));
--echo # End of 10.6 tests

View File

@@ -0,0 +1,8 @@
#
# MDEV-37000 Deleting a row from Aria table results to 'Index is corrupt'
#
CREATE OR REPLACE TABLE t (a INT, b CHAR(8) NOT NULL, KEY(b)) ENGINE=Aria DEFAULT CHARSET=utf8;
INSERT INTO t VALUES (1,'20070101'), (2,'20070101');
DELETE FROM t LIMIT 2;
DROP TABLE t;
# End of 10.6 tests

View File

@@ -0,0 +1,9 @@
--echo #
--echo # MDEV-37000 Deleting a row from Aria table results to 'Index is corrupt'
--echo #
CREATE OR REPLACE TABLE t (a INT, b CHAR(8) NOT NULL, KEY(b)) ENGINE=Aria DEFAULT CHARSET=utf8;
INSERT INTO t VALUES (1,'20070101'), (2,'20070101');
DELETE FROM t LIMIT 2;
DROP TABLE t;
--echo # End of 10.6 tests

View File

@@ -245,6 +245,14 @@ DECLARE a mysql_json;
END;
$$
ERROR HY000: 'MYSQL_JSON' is not allowed in this context
#
# End of 10.5 tests
#
# MDEV-40678 mysql_json plugin OOB reads
#
alter table t1 force;
ERROR HY000: Error parsing MySQL JSON format, please dump this table from MySQL and then restore it to be able to use it in MariaDB.
flush tables;
alter table t1 force;
ERROR HY000: Error parsing MySQL JSON format, please dump this table from MySQL and then restore it to be able to use it in MariaDB.
drop table t1;
# End of 10.6 tests

View File

@@ -17,7 +17,7 @@ call mtr.add_suppression("Table rebuild required");
call mtr.add_suppression("is marked as crashed");
call mtr.add_suppression("Checking");
let $MYSQLD_DATADIR= `select @@datadir`;
let MYSQLD_DATADIR= `select @@datadir`;
SET NAMES utf8;
@@ -161,6 +161,40 @@ END;
$$
DELIMITER ;$$
--echo #
--echo # End of 10.5 tests
--echo #
--echo # MDEV-40678 mysql_json plugin OOB reads
--echo #
--copy_file std_data/mysql_json/mysql_json_test.frm $MYSQLD_DATADIR/test/t1.frm
--copy_file std_data/mysql_json/mysql_json_test.MYI $MYSQLD_DATADIR/test/t1.MYI
--copy_file std_data/mysql_json/mysql_json_test.MYD $MYSQLD_DATADIR/test/t1.MYD
perl;
my $f="$ENV{MYSQLD_DATADIR}/test/t1.MYD";
open F, '+<', $f or die "open($f): $!";
sysseek F, 0x3c5, 0 or die "sysseek: $!";
syswrite F, '(' or die "syswrite: $!";
EOF
--error ER_UNKNOWN_ERROR
alter table t1 force;
flush tables;
--remove_file $MYSQLD_DATADIR/test/t1.MYD
--copy_file std_data/mysql_json/mysql_json_test.MYD $MYSQLD_DATADIR/test/t1.MYD
perl;
my $f="$ENV{MYSQLD_DATADIR}/test/t1.MYD";
open F, '+<', $f or die "open($f): $!";
sysseek F, 0x3bc, 0 or die "sysseek: $!";
syswrite F, '(' or die "syswrite: $!";
EOF
--error ER_UNKNOWN_ERROR
alter table t1 force;
drop table t1;
--echo # End of 10.6 tests

View File

@@ -16,7 +16,17 @@
--net-buffer-length=#
Buffer length for TCP/IP and socket communication
--net-read-timeout=#
@@ -1374,6 +1376,10 @@
@@ -1216,8 +1218,7 @@
(Defaults to on; use --skip-secure-auth to disable.)
--secure-file-priv=name
Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to
- files within specified directory. Empty value means no
- limits except /proc
+ files within specified directory.
--secure-timestamp=name
Restricts direct setting of a session timestamp. Possible
levels are: YES - timestamp cannot deviate from the
@@ -1375,6 +1376,10 @@
Alias for log_slow_query_file. Log slow queries to given
log file. Defaults logging to 'hostname'-slow.log. Must
be enabled to activate other slow log options
@@ -27,7 +37,7 @@
--socket=name Socket file to use for connection
--sort-buffer-size=#
Each thread that needs to do a sort allocates a buffer of
@@ -1394,6 +1400,7 @@
@@ -1395,6 +1400,7 @@
aborted. Prevents the common mistake of accidentally
deleting or updating every row in a table.
--stack-trace Print a symbolic stack trace on failure
@@ -35,7 +45,7 @@
--standard-compliant-cte
Allow only CTEs compliant to SQL standard
(Defaults to on; use --skip-standard-compliant-cte to disable.)
@@ -1472,6 +1479,12 @@
@@ -1473,6 +1479,12 @@
--thread-pool-max-threads=#
Maximum allowed number of worker threads in the thread
pool
@@ -48,7 +58,7 @@
--thread-pool-oversubscribe=#
How many additional active worker threads in a group are
allowed.
@@ -1510,8 +1523,8 @@
@@ -1511,8 +1523,8 @@
automatically convert it to an on-disk MyISAM or Aria
table.
-t, --tmpdir=name Path for temporary files. Several paths may be specified,
@@ -59,7 +69,7 @@
--transaction-alloc-block-size=#
Allocation block size for transactions to be stored in
binary log
@@ -1731,6 +1744,7 @@
@@ -1732,6 +1744,7 @@
myisam-stats-method NULLS_UNEQUAL
myisam-use-mmap FALSE
mysql56-temporal-format TRUE
@@ -67,7 +77,7 @@
net-buffer-length 16384
net-read-timeout 30
net-retry-count 10
@@ -1891,6 +1905,7 @@
@@ -1892,6 +1905,7 @@
slave-type-conversions
slow-launch-time 2
slow-query-log FALSE
@@ -75,7 +85,7 @@
sort-buffer-size 2097152
sql-mode STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
sql-safe-updates FALSE
@@ -1917,6 +1932,8 @@
@@ -1918,6 +1932,8 @@
thread-pool-exact-stats FALSE
thread-pool-idle-timeout 60
thread-pool-max-threads 65536

View File

@@ -1216,7 +1216,8 @@ The following specify which files/extra groups are read (specified before remain
(Defaults to on; use --skip-secure-auth to disable.)
--secure-file-priv=name
Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to
files within specified directory
files within specified directory. Empty value means no
limits except /proc
--secure-timestamp=name
Restricts direct setting of a session timestamp. Possible
levels are: YES - timestamp cannot deviate from the

View File

@@ -1023,6 +1023,12 @@ the false block has written nothing
mysqltest: At line 1: End of file encountered before 'eval' delimiter was found
# Only 'eval' is allowed as the third argument
mysqltest: At line 1: Invalid argument 'junk' to 'write_file', only 'eval' is allowed
#
# \0 in a query
#
select hex("12");
hex("1\x002")
310032
# End of 10.6 tests
#
# MDEV-29344: engines/iuds.insert_time cannot run with PS protocol (syntax error)

View File

@@ -3054,6 +3054,11 @@ EOF
--error 1
--exec echo "--write_file $file EOF junk" | $MYSQL_TEST 2>&1
--echo #
--echo # \0 in a query
--echo #
select hex("12");
--echo # End of 10.6 tests
--echo #
@@ -3077,12 +3082,11 @@ SELECT 1 /* doesn't throw error */;
--rmdir $srcdir
--echo # End of 10.11 tests
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
--disable_query_log
--eval SET GLOBAL max_connections = $saved_max_connections
--enable_query_log
--echo # End of 10.11 tests

View File

@@ -505,4 +505,11 @@ mysqltest: At line 1: Evaluation error: Modulo by zero
# Test case: Overflow
mysqltest: At line 1: Range error: 18446744073709551616 value out of range for Integer type
mysqltest: At line 1: Range error: 18446744073709551616 value out of range for Integer type
#
# MDEV-37865: IF() function is returning incorrect error
#
# Test case: unknown function name that is a prefix of a known one
mysqltest: At line 1: Syntax error: Unknown function 'if'
mysqltest: At line 1: Syntax error: Unknown function 'l'
# Expression evaluation tests completed successfully
# End of 10.6 tests

View File

@@ -894,4 +894,17 @@ let $large_xor = $(0x7FFFFFFFFFFFFFFF ^ 0x7FFFFFFFFFFFFFFE);
--error 1
--exec echo "let \$a = \$(-18446744073709551616);" | $MYSQL_TEST 2>&1
--echo #
--echo # MDEV-37865: IF() function is returning incorrect error
--echo #
--echo # Test case: unknown function name that is a prefix of a known one
--error 1
--exec echo "let \$a = \$(if(null, 'fallback', 'actual'));" | $MYSQL_TEST 2>&1
--error 1
--exec echo "let \$a = \$(l('AB'));" | $MYSQL_TEST 2>&1
--echo # Expression evaluation tests completed successfully
--echo # End of 10.6 tests

View File

@@ -1116,4 +1116,11 @@ insert(upper('hello world'), 7, 5, lower('MARIADB')) -> HELLO mariadb
concat_ws(' ', upper('hello'), lower('WORLD')) -> HELLO world
ifnull(nullif('same', 'same'), 'default') -> default
coalesce(nullif('test', 'test'), 'backup') -> backup
#
# MDEV-37859: Mysqltest hex() not working for string values
#
hex("abc") -> 616263
hex("255") -> 323535
hex('') ->
# Expression evaluation string functions tests completed successfully
# End of 10.6 tests

View File

@@ -2128,4 +2128,19 @@ let $result5 = $(ifnull(nullif('same', 'same'), 'default'));
let $result6 = $(coalesce(nullif('test', 'test'), 'backup'));
--echo coalesce(nullif('test', 'test'), 'backup') -> $result6
--echo #
--echo # MDEV-37859: Mysqltest hex() not working for string values
--echo #
let $hex_str1 = $(hex("abc"));
--echo hex("abc") -> $hex_str1
let $hex_str2 = $(hex("255"));
--echo hex("255") -> $hex_str2
let $hex_str3 = $(hex(''));
--echo hex('') -> $hex_str3
--echo # Expression evaluation string functions tests completed successfully
--echo # End of 10.6 tests

View File

@@ -0,0 +1,18 @@
CHANGE MASTER TO master_host='127.0.0.1', master_user='root';
START SLAVE SQL_THREAD;
include/wait_for_slave_sql_to_start.inc
SET @@SESSION.debug_sync=
'hold_sss_with_run_lock SIGNAL sss_got_run_lock WAIT_FOR sss_continue';
SHOW SLAVE STATUS;
connect stopper, 127.0.0.1, root, , , $SERVER_MYPORT_1;
SET @@SESSION.debug_sync= 'now WAIT_FOR sss_got_run_lock';
STOP SLAVE SQL_THREAD;
connect continuer, 127.0.0.1, root, , , $SERVER_MYPORT_1;
SET @@SESSION.debug_sync= 'now SIGNAL sss_continue';
connection stopper;
disconnect continuer;
connection default;
disconnect stopper;
SET @@SESSION.debug_sync= RESET;
include/wait_for_slave_sql_to_stop.inc
RESET SLAVE ALL;

View File

@@ -0,0 +1,43 @@
# MDEV-40298
# Use-After-Free when SQL Thread stops in the middle of SHOW SLAVE STATUS
#
# Test that the correct synchronization is performed
--source include/have_debug_sync.inc
--source include/have_binlog_format_mixed.inc # No actual replication required
CHANGE MASTER TO master_host='127.0.0.1', master_user='root'; # basic setup
START SLAVE SQL_THREAD;
--source include/wait_for_slave_sql_to_start.inc
SET @@SESSION.debug_sync=
'hold_sss_with_run_lock SIGNAL sss_got_run_lock WAIT_FOR sss_continue';
--send SHOW SLAVE STATUS
--connect (stopper, 127.0.0.1, root, , , $SERVER_MYPORT_1)
SET @@SESSION.debug_sync= 'now WAIT_FOR sss_got_run_lock';
--send STOP SLAVE SQL_THREAD
# Wait a bit for the thread to "shut down"
# (It should not shut down, but block waiting for the ongoing SSS;
# the tested bug instead pulled this carpet under the SSS's feet.)
--sleep 1
--connect (continuer, 127.0.0.1, root, , , $SERVER_MYPORT_1)
SET @@SESSION.debug_sync= 'now SIGNAL sss_continue';
--connection stopper
--disconnect continuer
--reap
--connection default
--disconnect stopper
--disable_result_log
--reap
--enable_result_log
# Clean-up
SET @@SESSION.debug_sync= RESET;
--source include/wait_for_slave_sql_to_stop.inc
RESET SLAVE ALL;

View File

@@ -1414,11 +1414,4 @@ ExtractValue(@xml, '/employee/dt3')
NULL
Warnings:
Warning 1525 Incorrect XML value: 'parse error at line 10 pos 27: unexpected END-OF-INPUT'
#
# MDEV-39750 ExtractValue does not control recursion depth.
#
SELECT ExtractValue('<a/>', REPEAT('(', 100000));
ERROR HY000: Thread stack overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed. Consider increasing the thread_stack system variable.
SELECT ExtractValue('<a/>', REPEAT('/n', 100000));
ERROR HY000: Thread stack overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed. Consider increasing the thread_stack system variable.
# End of 10.6 tests

View File

@@ -899,17 +899,5 @@ set @xml= '<?xml version="1.0"?>
SELECT ExtractValue(@xml, '/employee/dt3');
--echo #
--echo # MDEV-39750 ExtractValue does not control recursion depth.
--echo #
--replace_regex /overrun: [0-9]* bytes used of a [0-9]* byte stack, and [0-9]* bytes needed/overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed/
--error ER_STACK_OVERRUN_NEED_MORE
SELECT ExtractValue('<a/>', REPEAT('(', 100000));
--replace_regex /overrun: [0-9]* bytes used of a [0-9]* byte stack, and [0-9]* bytes needed/overrun: 'used bytes' used of a 'available' byte stack, and 'X' bytes needed/
--error ER_STACK_OVERRUN_NEED_MORE
SELECT ExtractValue('<a/>', REPEAT('/n', 100000));
--echo # End of 10.6 tests

View File

@@ -0,0 +1,12 @@
CREATE TABLE t1(
t TEXT NULL DEFAULT NULL
) COLLATE='utf8_general_ci' ENGINE=archive;
INSERT INTO t1(t) VALUES ('Testtext');
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair status OK
SELECT t = 'Testtext' as Expect_1 from t1;
Expect_1
1
DROP TABLE t1;
# End of 10.6 tests

View File

@@ -0,0 +1,10 @@
--source include/have_archive.inc
CREATE TABLE t1(
t TEXT NULL DEFAULT NULL
) COLLATE='utf8_general_ci' ENGINE=archive;
INSERT INTO t1(t) VALUES ('Testtext');
REPAIR TABLE t1;
SELECT t = 'Testtext' as Expect_1 from t1;
DROP TABLE t1;
--echo # End of 10.6 tests

View File

@@ -320,3 +320,26 @@ SESSION_USER() CURRENT_USER() msg
root@localhost root@localhost p1.p1
DROP PACKAGE p1;
DROP USER xxx@localhost;
#
# MDEV-40722 DROP PACKAGE leaves PACKAGE BODY grant in mysql.procs_priv
#
CREATE PACKAGE pkg1 AS
FUNCTION f RETURN VARCHAR2(64);
END
$$
CREATE PACKAGE BODY pkg1 AS
FUNCTION f RETURN VARCHAR2(64) AS
BEGIN RETURN 'f';
END;
END$$
CREATE USER reader@localhost;
GRANT EXECUTE ON PACKAGE test.pkg1 TO reader@localhost;
GRANT EXECUTE ON PACKAGE BODY test.pkg1 TO reader@localhost;
SELECT host,db,user,routine_name,routine_type,proc_priv FROM mysql.procs_priv ORDER BY routine_type;
host db user routine_name routine_type proc_priv
localhost test reader pkg1 PACKAGE Execute
localhost test reader pkg1 PACKAGE BODY Execute
DROP PACKAGE pkg1;
SELECT host,db,user,routine_name,routine_type,proc_priv FROM mysql.procs_priv ORDER BY routine_type;
host db user routine_name routine_type proc_priv
DROP USER reader@localhost;

View File

@@ -330,3 +330,28 @@ DELIMITER ;$$
CALL p1.p1;
DROP PACKAGE p1;
DROP USER xxx@localhost;
--echo #
--echo # MDEV-40722 DROP PACKAGE leaves PACKAGE BODY grant in mysql.procs_priv
--echo #
DELIMITER $$;
CREATE PACKAGE pkg1 AS
FUNCTION f RETURN VARCHAR2(64);
END
$$
CREATE PACKAGE BODY pkg1 AS
FUNCTION f RETURN VARCHAR2(64) AS
BEGIN RETURN 'f';
END;
END$$
DELIMITER ;$$
CREATE USER reader@localhost;
GRANT EXECUTE ON PACKAGE test.pkg1 TO reader@localhost;
GRANT EXECUTE ON PACKAGE BODY test.pkg1 TO reader@localhost;
SELECT host,db,user,routine_name,routine_type,proc_priv FROM mysql.procs_priv ORDER BY routine_type;
DROP PACKAGE pkg1;
SELECT host,db,user,routine_name,routine_type,proc_priv FROM mysql.procs_priv ORDER BY routine_type;
DROP USER reader@localhost;

View File

@@ -0,0 +1,11 @@
#
# MDEV-40636 CSV crashes on DELETE
#
create table t1 (id int not null, pad char(10) not null) engine=csv;
insert t1 select seq,'AAAAAAAAAA' from seq_1_to_2048;
delete from t1 where id % 2 = 0;
select count(*), sum(id) from t1;
count(*) sum(id)
1024 1048576
drop table t1;
# End of 10.6 tests

View File

@@ -0,0 +1,13 @@
--source include/have_csv.inc
--source include/have_sequence.inc
--echo #
--echo # MDEV-40636 CSV crashes on DELETE
--echo #
create table t1 (id int not null, pad char(10) not null) engine=csv;
insert t1 select seq,'AAAAAAAAAA' from seq_1_to_2048;
delete from t1 where id % 2 = 0;
select count(*), sum(id) from t1;
drop table t1;
--echo # End of 10.6 tests

View File

@@ -1,9 +1,46 @@
call mtr.add_suppression("File 'bad' not found");
call mtr.add_suppression("File 'bad.key' not found");
call mtr.add_suppression("Plugin 'file_key_management' init function returned error");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
FOUND 1 /File 'bad' not found/ in mysqld.1.err
FOUND 1 /File 'bad.key' not found/ in mysqld.1.err
create table t1(c1 bigint not null, b char(200)) engine=innodb encrypted=yes encryption_key_id=1;
ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options")
select plugin_status from information_schema.plugins
where plugin_name = 'file_key_management';
plugin_status
install soname 'file_key_management';
ERROR HY000: Cannot decrypt MTR_SUITE_DIR/t/filekeys-data.enc. Wrong key?
call mtr.add_suppression("Cannot decrypt .*filekeys-data.enc. Wrong key");
call mtr.add_suppression("Plugin 'file_key_management' init function returned error");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
FOUND 1 /Cannot decrypt .*filekeys-data.enc. Wrong key/ in mysqld.1.err
create table t1(c1 bigint not null, b char(200)) engine=innodb encrypted=yes encryption_key_id=1;
ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options")
select plugin_status from information_schema.plugins
where plugin_name = 'file_key_management';
plugin_status
install soname 'file_key_management';
ERROR HY000: Cannot read bad.key, the filekey is too long, max secret size is 256B
call mtr.add_suppression("the filekey is too long");
call mtr.add_suppression("Plugin 'file_key_management' init function returned error");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
FOUND 1 /the filekey is too long/ in mysqld.1.err
create table t1(c1 bigint not null, b char(200)) engine=innodb encrypted=yes encryption_key_id=1;
ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options")
select plugin_status from information_schema.plugins
where plugin_name = 'file_key_management';
plugin_status
#
# MDEV-40658 file_key_management crash on empty FILE: file
#
install soname 'file_key_management';
ERROR HY000: Cannot decrypt MTR_SUITE_DIR/t/filekeys-data.enc. Wrong key?
call mtr.add_suppression("Cannot decrypt .*filekeys-data.enc. Wrong key");
call mtr.add_suppression("Plugin 'file_key_management' init function returned error");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
FOUND 1 /Cannot decrypt .*filekeys-data.enc. Wrong key/ in mysqld.1.err
create table t1(c1 bigint not null, b char(200)) engine=innodb encrypted=yes encryption_key_id=1;
ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options")
select plugin_status from information_schema.plugins
where plugin_name = 'file_key_management';
plugin_status
# End of 10.6 tests

View File

@@ -1,10 +0,0 @@
call mtr.add_suppression("the filekey is too long");
call mtr.add_suppression("Plugin 'file_key_management' init function returned error");
call mtr.add_suppression("Plugin 'file_key_management' registration.*failed");
FOUND 1 /the filekey is too long/ in mysqld.1.err
create table t1(c1 bigint not null, b char(200)) engine=innodb encrypted=yes encryption_key_id=1;
ERROR HY000: Can't create table `test`.`t1` (errno: 140 "Wrong create options")
select plugin_status from information_schema.plugins
where plugin_name = 'file_key_management';
plugin_status
# Test checks if opening an too large secret does not crash the server.

View File

@@ -1,4 +0,0 @@
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret

View File

@@ -1,2 +1,2 @@
--loose-file-key-management-filekey=FILE:bad
--loose-file-key-management-filekey=FILE:bad.key
--loose-file-key-management-filename=$MTR_SUITE_DIR/t/filekeys-data.enc

View File

@@ -1,2 +1,51 @@
let SEARCH_PATTERN=File 'bad' not found;
source filekeys_badtest.inc;
#
# first test: no file
#
--let SEARCH_PATTERN=File 'bad.key' not found
--source filekeys_badtest.inc
#
# second test: wrong key
#
--let $datadir=`select @@datadir`
--write_file $datadir/bad.key
wrong key
EOF
--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR
--error 2
install soname 'file_key_management';
--let SEARCH_PATTERN=Cannot decrypt .*filekeys-data.enc. Wrong key
--source filekeys_badtest.inc
--remove_file $datadir/bad.key
#
# third test: too long key
#
--let $datadir=`select @@datadir`
--write_file $datadir/bad.key
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret
EOF
--error 2
install soname 'file_key_management';
--let SEARCH_PATTERN=the filekey is too long
--source filekeys_badtest.inc
--remove_file $datadir/bad.key
--echo #
--echo # MDEV-40658 file_key_management crash on empty FILE: file
--echo #
--let $datadir=`select @@datadir`
--write_file $datadir/bad.key
EOF
--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR
--error 2
install soname 'file_key_management';
--let SEARCH_PATTERN=Cannot decrypt .*filekeys-data.enc. Wrong key
--source filekeys_badtest.inc
--remove_file $datadir/bad.key
--echo # End of 10.6 tests

View File

@@ -1,3 +0,0 @@
--loose-file-key-management-filekey=FILE:$MTR_SUITE_DIR/t/filekeys-data-too-long.key
--loose-file-key-management-filename=$MTR_SUITE_DIR/t/filekeys-data.enc

View File

@@ -1,4 +0,0 @@
let SEARCH_PATTERN=the filekey is too long;
source filekeys_badtest.inc;
--echo # Test checks if opening an too large secret does not crash the server.

View File

@@ -34,6 +34,8 @@ wsrep_node_incoming_address=127.0.0.1:@mysqld.2.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.2.#sst_port'
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -48,6 +48,8 @@ wsrep-on=OFF
server-id=3
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -54,6 +54,8 @@ gtid_domain_id=4
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -47,6 +47,8 @@ wsrep-on=OFF
server-id=3
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -61,6 +61,8 @@ wsrep_node_incoming_address=127.0.0.1:@mysqld.4.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.4.#sst_port'
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -60,6 +60,8 @@ wsrep-on=OFF
server-id=4
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -61,6 +61,8 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.4.#sst_port'
auto-increment-offset=4
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -0,0 +1,4 @@
#
# suite.pm will make sure that all tests including this file
# will be skipped as needed
#

View File

@@ -0,0 +1,27 @@
connection node_2;
connection node_1;
connection node_1;
connection node_2;
connection node_1;
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
connection node_2;
# Force a fresh SST on node_2
# Start node_2 with SSL cert/key simulated as missing
# node_2 failed to start, as expected
# Joiner refused the SST instead of running unencrypted
FOUND 1 /ssl-mode is set to .REQUIRED., but no usable SSL/ in mysqld.2.err
# Restart node_2 normally so it rejoins
connection node_2;
call mtr.add_suppression("WSREP_SST:");
call mtr.add_suppression("WSREP: Process completed with error:");
call mtr.add_suppression("WSREP: Failed to read .ready <addr>.");
call mtr.add_suppression("WSREP: Failed to read uuid:seqno and wsrep_gtid_domain_id from joiner script.");
call mtr.add_suppression("WSREP: Failed to read state from: .*");
call mtr.add_suppression("WSREP: Failed to prepare for .*");
call mtr.add_suppression("WSREP: SST failed:.*");
call mtr.add_suppression("WSREP: SST request callback failed. This is unrecoverable, restart required.");
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP: Requesting state transfer failed:.*");
connection node_1;

View File

@@ -0,0 +1,27 @@
connection node_2;
connection node_1;
connection node_1;
connection node_2;
connection node_1;
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
connection node_2;
# Force a fresh SST on node_2
# Start node_2 with stunnel simulated as missing
# node_2 failed to start, as expected
# Joiner refused the SST instead of running unencrypted
FOUND 1 /ssl-mode is set to .REQUIRED., but the .stunnel. binary was not found/ in mysqld.2.err
# Restart node_2 normally so it rejoins
connection node_2;
call mtr.add_suppression("WSREP_SST:");
call mtr.add_suppression("WSREP: Process completed with error:");
call mtr.add_suppression("WSREP: Failed to read .ready <addr>.");
call mtr.add_suppression("WSREP: Failed to read uuid:seqno and wsrep_gtid_domain_id from joiner script.");
call mtr.add_suppression("WSREP: Failed to read state from: .*");
call mtr.add_suppression("WSREP: Failed to prepare for .*");
call mtr.add_suppression("WSREP: SST failed:.*");
call mtr.add_suppression("WSREP: SST request callback failed. This is unrecoverable, restart required.");
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP: Requesting state transfer failed:.*");
connection node_1;

View File

@@ -80,6 +80,10 @@ sub skip_combinations {
unless which("lsof") || which("sockstat") || which("ss");
$skip{'include/have_stunnel.inc'} = "Need 'stunnel' utility"
unless which("stunnel");
# 'mariabackup' combination doesn't need stunnel: only 'rsync' SST goes
# through it.
$skip{'t/galera_sst_cn_injection.combinations'} = [ 'rsync' ]
unless which("stunnel");
$skip{'include/have_qpress.inc'} = "Need 'qpress' utility"
unless which("qpress");
$skip{'../encryption/include/have_file_key_management_plugin.combinations'} = [ 'ctr' ]
@@ -93,6 +97,9 @@ sub skip_combinations {
$skip{'t/galera_sst_cn_injection.test'} = 'does not work with OpenSSL <= 1.1.1'
unless $openssl_ver ge "3.0.0";
$skip{'include/have_pkill.inc'} = "Need 'pkill' utility"
unless which("pkill");
%skip;
}

View File

@@ -1,6 +1,7 @@
--source include/big_test.inc
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_stunnel.inc
--let $node_1=node_1
--let $node_2=node_2

View File

@@ -10,6 +10,7 @@
--source include/have_mariabackup.inc
--source include/have_ssl_communication.inc
--source include/have_openssl.inc
--source include/have_pkill.inc
# This test asserts that the initial cluster-bootstrap SST was SSL-encrypted by
# grepping the donor's error log. That is a one-time event, so the cluster must
# be freshly bootstrapped on every run (e.g. with --repeat) for node_2 to SST
@@ -56,7 +57,8 @@ call mtr.add_suppression("WSREP: Illegal character in variable:");
--echo # cleanup
# we have to kill joiner's socat here, because the donor has aborted SST
# and joiner's socat will timeout in 5 minutes
# pkill exit code varies by platform 0 or 1
# pkill below might return 0 = success or 1 in error when it does not find socat
# anymore
--error 0,1,15
--exec pkill -f 'socat.*[s]erver-new-cert'
--exec echo ssl-cert=$MYSQL_TEST_DIR/std_data/server-cert.pem >> $MYSQLTEST_VARDIR/my.cnf

View File

@@ -0,0 +1,11 @@
!include ../galera_2nodes.cnf
[mysqld]
wsrep_sst_method=mariabackup
wsrep_sst_auth="root:"
ssl-cert=@ENV.MYSQL_TEST_DIR/std_data/server-cert.pem
ssl-key=@ENV.MYSQL_TEST_DIR/std_data/server-key.pem
ssl-ca=@ENV.MYSQL_TEST_DIR/std_data/cacert.pem
[sst]
ssl-mode=REQUIRED

View File

@@ -0,0 +1,69 @@
# MDEV-28233: with ssl-mode requiring encryption but no usable cert/key,
# wsrep_sst_mariabackup used to fall back to an unencrypted transfer
# WITHOUT any error or warning. Before the fix the joiner only logged the
# informational line:
# WSREP_SST: [INFO] SSL configuration: ... MODE='REQUIRED' ... encrypt='0'
# and then opened a cleartext socket.
#
# Steps:
# 1. Form a 2-node cluster with ssl-mode=REQUIRED and SSL cert/key.
# 2. Shut node_2 down and force a fresh SST.
# 3. Restart node_2 with cert/key simulated as missing
# (MTR_SST_SIMULATE_NO_SSL_CERT=1).
# 4. Check the joiner refuses the SST and logs the error.
# 5. Restart node_2 normally so it rejoins.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_mariabackup.inc
# Save auto_increment_offset values.
--let $node_1=node_1
--let $node_2=node_2
--source include/auto_increment_offset_save.inc
--connection node_1
# Donor-side noise when the joiner aborts the SST.
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
--connection node_2
--source include/shutdown_mysqld.inc
--echo # Force a fresh SST on node_2
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
--echo # Start node_2 with SSL cert/key simulated as missing
# Abort exit code varies by platform.
--error 1,134
--exec MTR_SST_SIMULATE_NO_SSL_CERT=1 $MYSQLD_LAST_CMD
--echo # node_2 failed to start, as expected
--echo # Joiner refused the SST instead of running unencrypted
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.2.err
--let SEARCH_PATTERN = ssl-mode is set to .REQUIRED., but no usable SSL
--source include/search_pattern_in_file.inc
--echo # Restart node_2 normally so it rejoins
--source include/start_mysqld.inc
--source include/wait_until_connected_again.inc
--connection node_2
# Joiner-side noise from the failed SST attempt.
call mtr.add_suppression("WSREP_SST:");
call mtr.add_suppression("WSREP: Process completed with error:");
call mtr.add_suppression("WSREP: Failed to read .ready <addr>.");
call mtr.add_suppression("WSREP: Failed to read uuid:seqno and wsrep_gtid_domain_id from joiner script.");
call mtr.add_suppression("WSREP: Failed to read state from: .*");
call mtr.add_suppression("WSREP: Failed to prepare for .*");
call mtr.add_suppression("WSREP: SST failed:.*");
call mtr.add_suppression("WSREP: SST request callback failed. This is unrecoverable, restart required.");
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP: Requesting state transfer failed:.*");
--connection node_1
--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc
--source include/auto_increment_offset_restore.inc

View File

@@ -0,0 +1,10 @@
!include ../galera_2nodes.cnf
[mysqld]
wsrep_sst_method=rsync
ssl-cert=@ENV.MYSQL_TEST_DIR/std_data/server-cert.pem
ssl-key=@ENV.MYSQL_TEST_DIR/std_data/server-key.pem
ssl-ca=@ENV.MYSQL_TEST_DIR/std_data/cacert.pem
[sst]
ssl-mode=REQUIRED

View File

@@ -0,0 +1,66 @@
# MDEV-28233: the rsync SST script must NOT silently fall back to an
# unencrypted transfer when encryption is requested (ssl-mode is set to a
# value other than DISABLED) but the stunnel binary is not installed.
#
# Steps:
# 1. Form a 2-node cluster with ssl-mode=REQUIRED (stunnel present).
# 2. Shut node_2 down and force a fresh SST.
# 3. Restart node_2 with stunnel hidden (MTR_SST_SIMULATE_NO_STUNNEL=1).
# 4. Check the joiner refuses the SST and logs the error.
# 5. Restart node_2 normally so it rejoins.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
# Needed for the initial SST; only simulated as missing below.
--source include/have_stunnel.inc
# Save auto_increment_offset values.
--let $node_1=node_1
--let $node_2=node_2
--source include/auto_increment_offset_save.inc
--connection node_1
# Donor-side noise when the joiner aborts the SST.
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
--connection node_2
--source include/shutdown_mysqld.inc
--echo # Force a fresh SST on node_2
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
--echo # Start node_2 with stunnel simulated as missing
# Abort exit code
--error 1,134
--exec MTR_SST_SIMULATE_NO_STUNNEL=1 $MYSQLD_LAST_CMD
--echo # node_2 failed to start, as expected
--echo # Joiner refused the SST instead of running unencrypted
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.2.err
--let SEARCH_PATTERN = ssl-mode is set to .REQUIRED., but the .stunnel. binary was not found
--source include/search_pattern_in_file.inc
--echo # Restart node_2 normally so it rejoins
--source include/start_mysqld.inc
--source include/wait_until_connected_again.inc
--connection node_2
# Joiner-side noise from the failed SST attempt.
call mtr.add_suppression("WSREP_SST:");
call mtr.add_suppression("WSREP: Process completed with error:");
call mtr.add_suppression("WSREP: Failed to read .ready <addr>.");
call mtr.add_suppression("WSREP: Failed to read uuid:seqno and wsrep_gtid_domain_id from joiner script.");
call mtr.add_suppression("WSREP: Failed to read state from: .*");
call mtr.add_suppression("WSREP: Failed to prepare for .*");
call mtr.add_suppression("WSREP: SST failed:.*");
call mtr.add_suppression("WSREP: SST request callback failed. This is unrecoverable, restart required.");
call mtr.add_suppression("WSREP: .*State transfer to.* failed:");
call mtr.add_suppression("WSREP: Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP: Requesting state transfer failed:.*");
--connection node_1
--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc
--source include/auto_increment_offset_restore.inc

View File

@@ -85,6 +85,8 @@ wsrep_node_incoming_address=127.0.0.1:@mysqld.6.port
wsrep_sst_receive_address='127.0.0.1:@mysqld.6.#sst_port'
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
[ENV]

View File

@@ -50,6 +50,8 @@ wsrep_sst_receive_address='127.0.0.1:@mysqld.3.#sst_port'
wsrep_node_name=node3
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
sst-log-archive-dir=@ENV.MYSQLTEST_VARDIR/log
transferfmt=@ENV.MTR_GALERA_TFMT

View File

@@ -27,7 +27,7 @@ master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; insert into t1 values (0.5)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # User var # #
master-bin.000001 # User var # # NULL
master-bin.000001 # Query # # use `test`; insert into t1 values (@a)
master-bin.000001 # Query # # COMMIT
set @@session.debug_dbug= "";

View File

@@ -0,0 +1,10 @@
--- secure_file_priv.result
+++ secure_file_priv.reject
@@ -30,6 +30,6 @@
#
CREATE TABLE t1 (c1 VARCHAR(50));
LOAD DATA INFILE '/proc/cpuinfo' INTO TABLE t1;
-ERROR HY000: The MariaDB server is running with the --secure-file-priv option so it cannot execute this statement
+ERROR HY000: File 'C:\proc\cpuinfo' not found (Errcode: 2 "No such file or directory")
DROP TABLE t1;
# End of 10.6 tests

View File

@@ -6,14 +6,17 @@ INSERT INTO t1 VALUES ("one"),("two"),("three"),("four"),("five");
SHOW VARIABLES LIKE 'secure_file_priv';
Variable_name Value
secure_file_priv
Warnings:
Warning 1287 '<select expression> INTO <destination>;' is deprecated and will be removed in a future release. Please use 'SELECT <select list> INTO <destination> FROM...' instead
SELECT * INTO OUTFILE '$MYSQLTEST_VARDIR/../bug50373.txt' FROM t1;
DELETE FROM t1;
LOAD DATA INFILE '$MYSQLTEST_VARDIR/../bug50373.txt' INTO TABLE t1;
SELECT * FROM t1;
c1
one
two
three
four
five
SELECT load_file('$MYSQLTEST_VARDIR/../bug50373.txt') AS loaded_file;
loaded_file
one
two
@@ -22,3 +25,11 @@ four
five
DROP TABLE t1;
#
# MDEV-40589 default exclude list for secure-file-priv
#
CREATE TABLE t1 (c1 VARCHAR(50));
LOAD DATA INFILE '/proc/cpuinfo' INTO TABLE t1;
ERROR HY000: The MariaDB server is running with the --secure-file-priv option so it cannot execute this statement
DROP TABLE t1;
# End of 10.6 tests

View File

@@ -3175,7 +3175,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME SECURE_FILE_PRIV
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE VARCHAR
VARIABLE_COMMENT Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory
VARIABLE_COMMENT Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory. Empty value means no limits except /proc
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL

View File

@@ -3645,7 +3645,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME SECURE_FILE_PRIV
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE VARCHAR
VARIABLE_COMMENT Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory
VARIABLE_COMMENT Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory. Empty value means no limits except /proc
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL

View File

@@ -1,10 +1,10 @@
--source include/platform.inc
--echo #
--echo # Bug50373 --secure-file-priv=""
--echo #
CREATE TABLE t1 (c1 VARCHAR(50));
INSERT INTO t1 VALUES ("one"),("two"),("three"),("four"),("five");
SHOW VARIABLES LIKE 'secure_file_priv';
--disable_query_log
# Atempt to create a file where we normally aren't allowed to create one.
#
@@ -17,35 +17,23 @@ SHOW VARIABLES LIKE 'secure_file_priv';
# If we run tests with --mem, it will be /dev/shm.
# If we run tests with --parallel, it will be mysql-test/var
# (because MYSQLTEST_VARDIR in this case is mysql-test/var/N).
--disable_cursor_protocol
--perl
use File::Basename;
my $protected_file= dirname($ENV{MYSQLTEST_VARDIR}).'/bug50373.txt';
# Ensure bug50373.txt does not exist (e.g. leftover from previous
# test runs).
unlink $protected_file;
open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/bug50373.inc") or die;
print FILE "SELECT * FROM t1 INTO OUTFILE '".$protected_file."';\n";
print FILE "DELETE FROM t1;\n";
print FILE "LOAD DATA INFILE '".$protected_file."' INTO TABLE t1;\n";
print FILE "SELECT * FROM t1;\n";
print FILE "SELECT load_file('",$protected_file,"') AS loaded_file;\n";
close(FILE);
EOF
--enable_prepare_warnings
--source $MYSQL_TMP_DIR/bug50373.inc
--disable_prepare_warnings
--remove_file $MYSQL_TMP_DIR/bug50373.inc
--enable_query_log
--enable_cursor_protocol
evalp SELECT * INTO OUTFILE '$MYSQLTEST_VARDIR/../bug50373.txt' FROM t1;
DELETE FROM t1;
evalp LOAD DATA INFILE '$MYSQLTEST_VARDIR/../bug50373.txt' INTO TABLE t1;
SELECT * FROM t1;
evalp SELECT load_file('$MYSQLTEST_VARDIR/../bug50373.txt') AS loaded_file;
DROP TABLE t1;
--perl
use File::Basename;
unlink dirname($ENV{MYSQLTEST_VARDIR}).'/bug50373.txt';
EOF
--remove_file $MYSQLTEST_VARDIR/../bug50373.txt
--echo #
--echo # MDEV-40589 default exclude list for secure-file-priv
--echo #
CREATE TABLE t1 (c1 VARCHAR(50));
--disable_abort_on_error
LOAD DATA INFILE '/proc/cpuinfo' INTO TABLE t1;
--enable_abort_on_error
DROP TABLE t1;
--echo # End of 10.6 tests

View File

@@ -175,7 +175,7 @@ bool Parser::read_filekey(const char *filekey, char *secret)
}
close(f);
while (secret[len - 1] == '\r' || secret[len - 1] == '\n') len--;
while (len && (secret[len - 1] == '\r' || secret[len - 1] == '\n')) len--;
if (len > MAX_SECRET_SIZE)
{
my_printf_error(EE_READ,

View File

@@ -233,6 +233,7 @@ static int qc_info_fill_table(THD *thd, TABLE_LIST *tables,
compile_time_assert(QUERY_CACHE_DB_LENGTH_SIZE == 2);
db= key + statement_text_length + 1 + QUERY_CACHE_DB_LENGTH_SIZE;
db_length= uint2korr(db - QUERY_CACHE_DB_LENGTH_SIZE);
set_if_smaller(db_length, (size_t)(key + key_length - QUERY_CACHE_FLAGS_SIZE - db));
table->field[COLUMN_STATEMENT_SCHEMA]->store(db, db_length, scs);

View File

@@ -373,7 +373,8 @@ static bool parse_mysql_scalar_or_value(String *buffer, const uchar *data,
if (type_is_stored_inline(value_type, large))
{
const size_t value_start = value_type_offset + 1;
if (parse_mysql_scalar(buffer, value_type, data + value_start,
if (value_start >= len ||
parse_mysql_scalar(buffer, value_type, data + value_start,
len - value_start))
return true;
}
@@ -383,7 +384,8 @@ static bool parse_mysql_scalar_or_value(String *buffer, const uchar *data,
of the Object / Array */
const size_t value_start= read_offset_or_size(
data + value_type_offset + 1, large);
if (parse_mysql_json_value(buffer, value_type, data + value_start,
if (value_start >= len ||
parse_mysql_json_value(buffer, value_type, data + value_start,
len - value_start, depth))
return true;
}
@@ -453,7 +455,7 @@ static bool parse_array_or_object(String *buffer, const uchar *data, size_t len,
i * value_size(large));
/* First print the key. */
if (buffer->append('"') ||
if (key_start + key_len > bytes || buffer->append('"') ||
append_string_json(buffer, data + key_start, key_len) ||
buffer->append(STRING_WITH_LEN("\": ")))
{

View File

@@ -26,6 +26,10 @@ wsrep_provider_options='base_port=@mysqld.2.#galera_port'
wsrep_sst_receive_address='127.0.0.1:@mysqld.2.#sst_port'
wsrep_node_name=test-node-2
[sst]
# SSL mode is disabled for SST unless tests specifically enable it.
ssl-mode=DISABLED
[ENV]
NODE_MYPORT_1= @mysqld.1.port
NODE_MYSOCK_1= @mysqld.1.socket

View File

@@ -21,11 +21,17 @@ EOF
exit 0
fi
echo _WSREP_NEW_CLUSTER='--wsrep-new-cluster' > "@INSTALL_RUNDATADIR@/wsrep-new-cluster" && \
# INSTALL_RUNDIR, not the socket directory - the latter is writable by
# mariadbd, which could then inject arbitrary variables into the service
# environment. Removing the file here and not in the service file,
# because that runs as mysql and cannot unlink in /run.
new_cluster=@INSTALL_RUNDIR@/mariadb-wsrep-new-cluster
trap 'rm -f "$new_cluster"' EXIT
trap 'exit 1' HUP INT TERM
echo _WSREP_NEW_CLUSTER='--wsrep-new-cluster' > "$new_cluster" && \
systemctl restart "${1:-mariadb.service}"
extcode=$?
rm -f "@INSTALL_RUNDATADIR@/wsrep-new-cluster"
exit $extcode

View File

@@ -571,6 +571,11 @@ read_cnf()
if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ]; then
check_server_ssl_config
fi
# MTR test hook: simulate a missing SSL certificate and key.
if [ -n "${MTR_SST_SIMULATE_NO_SSL_CERT:-}" ]; then
tpem=""
tkey=""
fi
if [ "$tmode" != 'DISABLED' ]; then
if [ 0 -eq $encrypt -a -n "$tpem" -a -n "$tkey" ]
then
@@ -599,6 +604,16 @@ read_cnf()
"CERT='$tpem', KEY='$tkey', MODE='$tmode'," \
"encrypt='$encrypt'"
# ssl-mode requires encryption but none could be set up (no usable
# cert/key): abort instead of silently transferring in cleartext.
if [ "$tmode" != 'DISABLED' -a $encrypt -eq 0 ]; then
wsrep_log_error "ssl-mode is set to '$tmode', but no usable SSL" \
"certificate and key were found. Cannot perform an" \
"encrypted transfer. Please configure ssl-cert and" \
"ssl-key, or set ssl-mode to DISABLED."
exit 22 # EINVAL
fi
if [ $encrypt -ge 2 ]; then
ssl_dhparams=$(parse_cnf "$encgroups" 'ssl-dhparams')
fi

View File

@@ -197,14 +197,11 @@ SSTCAP="$tcap"
SSLMODE=$(parse_cnf "$encgroups" 'ssl-mode' | tr '[[:lower:]]' '[[:upper:]]')
if [ -z "$SSLMODE" ]; then
# Implicit verification if CA is set and the SSL mode
# is not specified by user:
# ssl-mode not set: derive it from the SSL config. Set it even when
# stunnel is absent, so the check below aborts instead of silently
# falling back to an unencrypted transfer.
if [ -n "$SSTCA$SSTCAP" ]; then
STUNNEL_BIN=$(commandex 'stunnel')
if [ -n "$STUNNEL_BIN" ]; then
SSLMODE='VERIFY_CA'
fi
# Require SSL by default if SSL key and cert are present:
SSLMODE='VERIFY_CA'
elif [ -n "$SSTKEY" -a -n "$SSTCERT" ]; then
SSLMODE='REQUIRED'
fi
@@ -279,10 +276,22 @@ if [ -n "$SSLMODE" -a "$SSLMODE" != 'DISABLED' ]; then
if [ -z "${STUNNEL_BIN+x}" ]; then
STUNNEL_BIN=$(commandex 'stunnel')
fi
# MTR test hook: simulate a missing stunnel binary.
if [ -n "${MTR_SST_SIMULATE_NO_STUNNEL:-}" ]; then
STUNNEL_BIN=""
fi
if [ -n "$STUNNEL_BIN" ]; then
wsrep_log_info "Using stunnel for SSL encryption: CA: '$SSTCA'," \
"CAPATH='$SSTCAP', ssl-mode='$SSLMODE'"
STUNNEL="$STUNNEL_BIN $STUNNEL_CONF"
else
# Encryption required but stunnel missing: abort instead of
# silently falling back to an unencrypted transfer.
wsrep_log_error "ssl-mode is set to '$SSLMODE', but the 'stunnel'" \
"binary was not found in the path. Cannot perform" \
"an encrypted transfer. Please install stunnel or" \
"set ssl-mode to DISABLED."
exit 2 # ENOENT
fi
fi

View File

@@ -163,7 +163,8 @@ String *Item_func_geometry_from_json::val_str(String *str)
(const uchar *) js->end());
je.killed_ptr= (uint32_t *) &current_thd->killed;
if ((null_value= !Geometry::create_from_json(&buffer, &je, options==1, str)))
if (!json_read_value(&je) &&
(null_value= !Geometry::create_from_json(&buffer, &je, options==1, str)))
{
int code= 0;

View File

@@ -9126,45 +9126,39 @@ fn_format_relative_to_data_home(char * to, const char *name,
bool is_secure_file_path(char *path)
{
char buff1[FN_REFLEN], buff2[FN_REFLEN];
size_t opt_secure_file_priv_len;
/*
All paths are secure if opt_secure_file_path is 0
*/
if (!opt_secure_file_priv)
return TRUE;
char buf1[FN_REFLEN], buf2[FN_REFLEN];
const char *cmp;
opt_secure_file_priv_len= strlen(opt_secure_file_priv);
if (opt_secure_file_priv)
cmp= opt_secure_file_priv;
else
#ifdef _WIN32
return TRUE; // All paths are secure if opt_secure_file_priv is unset
#else
cmp= "/proc/"; // Check that it doesn't start with this prefix
#endif
if (strlen(path) >= FN_REFLEN)
return FALSE;
if (my_realpath(buff1, path, 0))
if (my_realpath(buf1, path, 0))
{
/*
The supplied file path might have been a file and not a directory.
*/
size_t length= dirname_length(path); // Guaranteed to be < FN_REFLEN
memcpy(buff2, path, length);
buff2[length]= '\0';
if (length == 0 || my_realpath(buff1, buff2, 0))
return FALSE;
}
convert_dirname(buff2, buff1, NullS);
if (!lower_case_file_system)
{
if (strncmp(opt_secure_file_priv, buff2, opt_secure_file_priv_len))
/* The supplied file path might have been a file and not a directory. */
size_t length= dirname_length(path); // Guaranteed to be < FN_REFLEN
memcpy(buf2, path, length);
buf2[length]= '\0';
if (length == 0 || my_realpath(buf1, buf2, 0))
return FALSE;
}
convert_dirname(buf2, buf1, NullS);
size_t cmp_len= strlen(cmp);
bool matched;
if (lower_case_file_system)
matched= !files_charset_info->strnncoll(buf2, strlen(buf2), cmp, cmp_len, 1);
else
{
if (files_charset_info->strnncoll(buff2, strlen(buff2),
opt_secure_file_priv,
opt_secure_file_priv_len,
TRUE))
return FALSE;
}
return TRUE;
matched= !strncmp(cmp, buf2, cmp_len);
return opt_secure_file_priv ? matched : !matched;
}

View File

@@ -3393,10 +3393,13 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store(mi->connection_name.str, mi->connection_name.length,
&my_charset_bin);
mysql_mutex_lock(&mi->run_lock);
mysql_mutex_lock(&mi->rli.run_lock);
THD *sql_thd= mi->rli.sql_driver_thd;
DEBUG_SYNC(thd, "hold_sss_with_run_lock");
const char *slave_sql_running_state=
sql_thd ? sql_thd->get_proc_info() : "";
mysql_mutex_unlock(&mi->rli.run_lock);
mysql_mutex_lock(&mi->run_lock);
THD *io_thd= mi->io_thd;
const char *slave_io_running_state= io_thd ? io_thd->get_proc_info() : "";
mysql_mutex_unlock(&mi->run_lock);

View File

@@ -493,19 +493,14 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
json_engine_t *je, bool er_on_3D, String *res)
{
Class_info *ci= NULL;
const uchar *coord_start= NULL, *geom_start= NULL,
*features_start= NULL, *geometry_start= NULL;
enum t_enum { T_NONE, T_GEOMETRY, T_GEOMETRIES, T_COORD, T_FEATURE };
enum t_enum arg= T_NONE;
json_engine_t argje, *je_arg;
Geometry *result;
uchar key_buf[max_keyname_len];
uint key_len;
int fcoll_type_found= 0, feature_type_found= 0;
bool feature_type_found= false;
const uint32_t *killed_ptr= (uint32_t *) je->killed_ptr;
int stack_p;
if (json_read_value(je))
goto err_return;
if (je->value_type != JSON_VALUE_OBJECT)
{
je->s.error= GEOJ_INCORRECT_GEOJSON;
@@ -514,7 +509,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
while (json_scan_next(je) == 0 && je->state != JST_OBJ_END)
{
DBUG_ASSERT(je->state == JST_KEY);
if (je->state != JST_KEY)
break;
key_len=0;
while (json_read_keyname_chr(je) == 0)
@@ -543,31 +539,36 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
if (je->value_type == JSON_VALUE_STRING)
{
if ((ci= find_class((const char *) je->value, je->value_len)))
if ((ci= find_class(reinterpret_cast<const char*>(je->value), je->value_len)))
{
if ((coord_start=
(ci == &geometrycollection_class) ? geom_start : coord_start))
if ((ci == &geometrycollection_class && arg == T_GEOMETRIES) || arg == T_COORD)
goto create_geom;
if (arg != T_NONE)
break; /* invalid arg present for current geometry */
}
else if (je->value_len == feature_coll_type_len &&
my_charset_latin1.strnncoll(je->value, je->value_len,
feature_coll_type, feature_coll_type_len) == 0)
feature_coll_type, feature_coll_type_len) == 0)
{
/*
'FeatureCollection' type found. Handle the 'Featurecollection'/'features'
GeoJSON construction.
'FeatureCollection' type found. Handle the 'Featurecollection'
/'features' GeoJSON construction.
*/
if (features_start)
goto handle_feature_collection;
fcoll_type_found= 1;
ci= &geometrycollection_class;
if (arg == T_FEATURE)
goto create_geom;
if (arg != T_NONE)
break; /* invalid arg present for current geometry */
}
else if (je->value_len == feature_type_len &&
my_charset_latin1.strnncoll(je->value, je->value_len,
feature_type, feature_type_len) == 0)
feature_type, feature_type_len) == 0)
{
if (geometry_start)
if (arg == T_GEOMETRY)
goto handle_geometry_key;
feature_type_found= 1;
feature_type_found= true;
if (arg != T_NONE)
break; /* invalid arg present for current geometry */
}
else /* can't understand the type. */
break;
@@ -578,6 +579,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
else if (key_len == coord_keyname_len &&
memcmp(key_buf, coord_keyname, coord_keyname_len) == 0)
{
if (arg != T_NONE)
break; /* previous arg unprocessed */
/*
Found the "coordinates" key. Let's check it's an array
and remember where it starts.
@@ -587,16 +590,25 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
if (je->value_type == JSON_VALUE_ARRAY)
{
coord_start= je->value_begin;
arg= T_COORD;
if (ci && ci != &geometrycollection_class)
goto create_geom;
{
je_arg= je;
goto create_geom;
}
argje= *je;
je_arg= &argje;
if (json_skip_level(je))
goto err_return;
}
else
break; /* coordinates needs to be an array */
}
else if (key_len == geometries_keyname_len &&
memcmp(key_buf, geometries_keyname, geometries_keyname_len) == 0)
{
if (arg != T_NONE)
break; /* previous arg unprocessed */
/*
Found the "geometries" key. Let's check it's an array
and remember where it starts.
@@ -606,17 +618,28 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
if (je->value_type == JSON_VALUE_ARRAY)
{
geom_start= je->value_begin;
if (ci == &geometrycollection_class)
{
coord_start= geom_start;
je_arg= je;
goto create_geom;
}
if (ci != nullptr)
break; /* geometries only valid inside geometrycollation */
arg= T_GEOMETRIES;
argje= *je;
je_arg= &argje;
/* skip geometries for now and search for type */
if (json_skip_level(je))
goto err_return;
}
else
break; /* geometries needs to be an array */
}
else if (key_len == features_keyname_len &&
memcmp(key_buf, features_keyname, features_keyname_len) == 0)
{
if (arg != T_NONE)
break; /* previous arg unprocessed */
/*
'features' key found. Handle the 'Featurecollection'/'features'
GeoJSON construction.
@@ -625,10 +648,23 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
goto err_return;
if (je->value_type == JSON_VALUE_ARRAY)
{
features_start= je->value_begin;
if (fcoll_type_found)
goto handle_feature_collection;
if (ci == &geometrycollection_class)
{
je_arg= je;
goto create_geom;
}
if (ci != nullptr)
break; /* features only valid inside featurecollation */
arg= T_FEATURE;
argje= *je;
je_arg= &argje;
/* skip features for now and search for type */
if (json_skip_level(je))
goto err_return;
}
else
break; /* feature collections needs to be an array */
}
else if (key_len == geometry_keyname_len &&
memcmp(key_buf, geometry_keyname, geometry_keyname_len) == 0)
@@ -637,12 +673,21 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
goto err_return;
if (je->value_type == JSON_VALUE_OBJECT)
{
geometry_start= je->value_begin;
if (feature_type_found)
{
je_arg= je;
goto handle_geometry_key;
}
if (ci != nullptr)
break; /* geometry only valid inside feature */
arg= T_GEOMETRY;
argje= *je;
je_arg= &argje;
if (json_skip_level(je))
goto err_return;
}
else
goto err_return;
break; /* geometry needs to be an object */
}
else
{
@@ -661,35 +706,35 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
}
goto err_return;
handle_feature_collection:
ci= &geometrycollection_class;
coord_start= features_start;
create_geom:
stack_p= je->stack_p;
json_scan_start(je, je->s.cs, coord_start, je->s.str_end);
je->killed_ptr= killed_ptr;
je->stack_p= stack_p;
if (res->reserve(1 + 4, 512))
goto err_return;
result= (*ci->m_create_func)(buffer->data);
res->q_append((char) wkb_ndr);
res->q_append((uint32) result->get_class_info()->m_type_id);
if (result->init_from_json(je, er_on_3D, res))
if (result->init_from_json(je_arg, er_on_3D, res))
{
if (je_arg != je) /* copy error out of copied engine */
*je= *je_arg;
goto err_return;
}
/* finish of the object scan for validation/geomcollection */
if (json_skip_level(je))
goto err_return;
return result;
handle_geometry_key:
stack_p= je->stack_p;
json_scan_start(je, je->s.cs, geometry_start, je->s.str_end);
je->killed_ptr= killed_ptr;
je->stack_p= stack_p;
return create_from_json(buffer, je, er_on_3D, res);
handle_geometry_key: /* feature */
result= create_from_json(buffer, je_arg, er_on_3D, res);
/* skip rest of feature - can be arbitrary fields */
if (json_skip_level(je))
goto err_return;
return result;
err_return:
return NULL;
}
@@ -964,7 +1009,6 @@ static int read_point_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
DBUG_ASSERT(je->state == JST_VALUE);
if (json_read_value(je))
return 1;
@@ -978,6 +1022,8 @@ static int read_point_from_json(json_engine_t *je, bool er_on_3D,
n_coord++;
}
if (je->s.error != 0)
return 1;
if (n_coord <= 2 || !er_on_3D)
return 0;
je->s.error= Geometry::GEOJ_DIMENSION_NOT_SUPPORTED;
@@ -991,8 +1037,6 @@ bad_coordinates:
bool Gis_point::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
{
double x, y;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
@@ -1006,6 +1050,7 @@ bool Gis_point::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
wkb->q_append(x);
wkb->q_append(y);
/* Note GEOJSON RFC7946 3.3.1 - 3D possible, but not WKB? */
return FALSE;
}
@@ -1282,11 +1327,9 @@ bool Gis_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
uint32 np_pos= wkb->length();
Gis_point p;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -1297,7 +1340,13 @@ bool Gis_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (p.init_from_json(je, er_on_3D, wkb))
return TRUE;
@@ -1676,11 +1725,9 @@ bool Gis_polygon::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
uint32 lr_pos= wkb->length();
int closed;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -1692,11 +1739,18 @@ bool Gis_polygon::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
Gis_line_string ls;
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
uint32 ls_pos=wkb->length();
if (ls.init_from_json(je, er_on_3D, wkb))
return TRUE;
ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
if (ls.is_closed(&closed) || !closed)
{
@@ -2197,11 +2251,9 @@ bool Gis_multi_point::init_from_json(json_engine_t *je, bool er_on_3D,
uint32 np_pos= wkb->length();
Gis_point p;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -2212,7 +2264,12 @@ bool Gis_multi_point::init_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (wkb->reserve(1 + 4, 512))
return TRUE;
@@ -2562,11 +2619,9 @@ bool Gis_multi_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
uint32 n_line_strings= 0;
uint32 ls_pos= wkb->length();
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -2577,9 +2632,14 @@ bool Gis_multi_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
Gis_line_string ls;
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
Gis_line_string ls;
if (wkb->reserve(1 + 4, 512))
return TRUE;
wkb->q_append((char) wkb_ndr);
@@ -2968,11 +3028,9 @@ bool Gis_multi_polygon::init_from_json(json_engine_t *je, bool er_on_3D,
int np_pos= wkb->length();
Gis_polygon p;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -2983,7 +3041,12 @@ bool Gis_multi_polygon::init_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (wkb->reserve(1 + 4, 512))
return TRUE;
@@ -3427,6 +3490,9 @@ uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
return 0;
n_geom= wkb_get_uint(wkb, bo);
if (check_stack_overrun(current_thd, STACK_MIN_SIZE, (uchar*)&wkb_orig))
return 1;
if (res->reserve(4, 512))
return 0;
res->q_append(n_geom);
@@ -3467,11 +3533,9 @@ bool Gis_geometry_collection::init_from_json(json_engine_t *je, bool er_on_3D,
Geometry_buffer buffer;
Geometry *g;
if (json_read_value(je))
return TRUE;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE;
}
@@ -3482,17 +3546,17 @@ bool Gis_geometry_collection::init_from_json(json_engine_t *je, bool er_on_3D,
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
json_engine_t sav_je= *je;
DBUG_ASSERT(je->state == JST_VALUE);
if (je->state == JST_VALUE)
{
if (json_read_value(je))
return TRUE;
}
if (je->state != JST_OBJ_START)
goto err_geoj_incorrect;
if (!(g= create_from_json(&buffer, je, er_on_3D, wkb)))
return TRUE;
*je= sav_je;
if (json_skip_array_item(je))
return TRUE;
n_objects++;
}

View File

@@ -3797,7 +3797,7 @@ privilege_t acl_get(const char *host, const char *ip,
acl_entry *entry;
DBUG_ENTER("acl_get");
tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
tmp_db= strmov(strmov(key, safe_str(ip ? ip : host)) + 1, user) + 1;
end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
if (end >= key + sizeof(key)) // db name was truncated
@@ -11922,6 +11922,61 @@ Silence_routine_definer_errors::handle_condition(
}
/*
The low level function to revoke routine privileges for the given sp handler
@param thd the thd
@param proc_privs the table mysql.proc_privs
@param sp_db the routine database
@param sp_name the routine name
@param sph the sp handler
*/
static void sp_revoke_privileges_for_handler(THD *thd, TABLE *proc_privs,
#if MYSQL_VERSION_ID < 110501
const char *sp_db,
const char *sp_name,
#else
#error Remove the above conditional code
const Lex_ident_db &sp_db,
const Lex_ident_routine &sp_name,
#endif
const Sp_handler *sph)
{
uint counter, revoked;
HASH *hash= sph->get_priv_hash();
do
{
for (counter= 0, revoked= 0 ; counter < hash->records ; )
{
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
#if MYSQL_VERSION_ID < 110501
if (!my_strcasecmp(&my_charset_utf8mb3_bin, grant_proc->db, sp_db) &&
!my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
#else
#error Remove the above conditional code
if (sp_db.streq(Lex_cstring_strlen(grant_proc->db)) &&
sp_name.streq(Lex_cstring_strlen(grant_proc->tname)))
#endif
{
LEX_USER lex_user;
lex_user.user.str= grant_proc->user;
lex_user.user.length= strlen(grant_proc->user);
lex_user.host.str= safe_str(grant_proc->host.hostname);
lex_user.host.length= strlen(lex_user.host.str);
if (replace_routine_table(thd, grant_proc,
proc_privs, lex_user,
grant_proc->db, grant_proc->tname,
sph, ALL_KNOWN_ACL, 1) == 0)
{
revoked= 1;
continue;
}
}
counter++;
}
} while (revoked);
}
/**
Revoke privileges for all users on a stored procedure. Use an error handler
that converts errors about missing grants into warnings.
@@ -11942,9 +11997,7 @@ Silence_routine_definer_errors::handle_condition(
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
const Sp_handler *sph)
{
uint counter, revoked;
int result;
HASH *hash= sph->get_priv_hash();
Silence_routine_definer_errors error_handler;
DBUG_ENTER("sp_revoke_privileges");
@@ -11964,31 +12017,12 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_mutex_lock(&acl_cache->lock);
/* Remove procedure access */
do
{
for (counter= 0, revoked= 0 ; counter < hash->records ; )
{
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
if (!my_strcasecmp(&my_charset_utf8mb3_bin, grant_proc->db, sp_db) &&
!my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
{
LEX_USER lex_user;
lex_user.user.str= grant_proc->user;
lex_user.user.length= strlen(grant_proc->user);
lex_user.host.str= safe_str(grant_proc->host.hostname);
lex_user.host.length= strlen(lex_user.host.str);
if (replace_routine_table(thd, grant_proc,
tables.procs_priv_table().table(), lex_user,
grant_proc->db, grant_proc->tname,
sph, ALL_KNOWN_ACL, 1) == 0)
{
revoked= 1;
continue;
}
}
counter++;
}
} while (revoked);
if (sph == &sp_handler_package_spec)
sp_revoke_privileges_for_handler(thd, tables.procs_priv_table().table(),
sp_db, sp_name, &sp_handler_package_body);
sp_revoke_privileges_for_handler(thd, tables.procs_priv_table().table(),
sp_db, sp_name, sph);
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);

View File

@@ -2247,7 +2247,7 @@ retry_share:
goto retry_share;
}
if (thd->open_tables && thd->open_tables->s->tdc->flushed)
if (!table_list->sequence && thd->open_tables && thd->open_tables->s->tdc->flushed)
{
/*
If the version changes while we're opening the tables,
@@ -5350,6 +5350,10 @@ bool open_and_lock_internal_tables(TABLE *table, bool lock_table)
MYSQL_LOCK_USE_MALLOC))
goto err;
/* no existing lock to merge with */
if (save_lock == nullptr)
DBUG_RETURN(0);
if (!(new_lock= mysql_lock_merge(save_lock, thd->lock)))
{
thd->lock= save_lock;

View File

@@ -781,10 +781,22 @@ bool select_unit_ext::send_eof()
next_sl &&
next_sl->get_linkage() == INTERSECT_TYPE &&
!next_sl->distinct;
/*
True at the last INTERSECT of an inline INTERSECT DISTINCT subsequence: the
records that did not match every operand (their intersect counter differs
from curr_step) must be dropped. Instead of a separate table scan this is
folded into the scan/unfold loops below, so the table is scanned only once
(MDEV-38722). When the subsequence is materialized in a derived table this
filtering is done by select_unit::send_eof() instead.
*/
bool filter_intersect= curr_op_type == INTERSECT_DISTINCT &&
!(next_sl &&
next_sl->get_linkage() == INTERSECT_TYPE);
bool need_unfold= disable_index_if_needed(curr_sl);
if (((curr_sl->distinct && !is_next_distinct) ||
curr_op_type == INTERSECT_ALL ||
filter_intersect ||
is_next_intersect_all) &&
!need_unfold)
{
@@ -806,6 +818,13 @@ bool select_unit_ext::send_eof()
}
break;
}
if (filter_intersect && additional_cnt->val_int() != curr_step)
{
/* drop a record that did not match every INTERSECT operand */
if (unlikely(error= delete_record()))
break;
continue;
}
store_record(table, record[1]);
if (curr_sl->distinct && !is_next_distinct)
@@ -859,6 +878,13 @@ bool select_unit_ext::send_eof()
}
break;
}
if (filter_intersect && additional_cnt->val_int() != curr_step)
{
/* drop a record that did not match every INTERSECT operand */
if (unlikely(error= delete_record()))
break;
continue;
}
dup_cnt= (ha_rows)duplicate_cnt->val_int();
/* delete record if not exist in the second operand */
if (dup_cnt == 0)
@@ -1999,6 +2025,15 @@ void st_select_lex_unit::optimize_bag_operation(bool is_outer_distinct)
sl->distinct= true;
disable_index= sl;
}
else if (disable_index)
{
/*
EXCEPT ALL needs the unique index in select_unit_ext::send_data().
If the index would otherwise be released at an earlier node,
postpone the release until (at least) this operation.
*/
disable_index= sl;
}
}
}
else

View File

@@ -3484,11 +3484,15 @@ static Sys_var_mybool Sys_require_secure_transport(
ON_CHECK(check_require_secure_transport), ON_UPDATE(0));
static Sys_var_charptr_fscs Sys_secure_file_priv(
"secure_file_priv",
"Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files "
"within specified directory",
PREALLOCATED READ_ONLY GLOBAL_VAR(opt_secure_file_priv),
CMD_LINE(REQUIRED_ARG, OPT_SEQURE_FILE_PRIV), DEFAULT(0));
"secure_file_priv",
"Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files "
"within specified directory."
#ifndef _WIN32
" Empty value means no limits except /proc"
#endif
,
PREALLOCATED READ_ONLY GLOBAL_VAR(opt_secure_file_priv),
CMD_LINE(REQUIRED_ARG, OPT_SEQURE_FILE_PRIV), DEFAULT(0));
static bool check_server_id(sys_var *self, THD *thd, set_var *var)
{

View File

@@ -114,10 +114,8 @@ static std::atomic<ulonglong> last_table_id;
/* Functions defined in this file */
static bool fix_type_pointers(const char ***typelib_value_names,
uint **typelib_value_lengths,
TYPELIB *point_to_type, uint types,
char *names, size_t names_length);
static bool fix_type_pointers(const char ***, const char **, uint **,
TYPELIB *, uint, char *, size_t);
static field_index_t find_field(Field **fields, uchar *record, uint start,
uint length);
@@ -770,7 +768,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
uint i, j, n_length;
KEY_PART_INFO *key_part= NULL;
ulong *rec_per_key= NULL;
KEY_PART_INFO *first_key_part= NULL;
KEY_PART_INFO *first_key_part= NULL, *key_part_end;
uint first_key_parts= 0;
if (!keys)
@@ -778,7 +776,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
if (!(keyinfo = (KEY*) alloc_root(&share->mem_root, len)))
return 1;
bzero((char*) keyinfo, len);
key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo);
key_part_end= key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo);
}
/*
@@ -834,6 +832,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
sizeof(ulong) * *ext_key_parts)))
return 1;
first_key_part= key_part;
key_part_end= key_part + *ext_key_parts;
first_key_parts= first_keyinfo->user_defined_key_parts;
keyinfo->flags= first_keyinfo->flags;
keyinfo->key_length= first_keyinfo->key_length;
@@ -847,6 +846,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
keyinfo->rec_per_key= rec_per_key;
for (j=keyinfo->user_defined_key_parts ; j-- ; key_part++)
{
if (key_part >= key_part_end)
return 1;
if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end)
return 1;
if (!(keyinfo->algorithm == HA_KEY_ALG_LONG_HASH))
@@ -904,6 +905,8 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
}
if (curr_key_part == curr_key_part_end)
{
if (key_part >= key_part_end)
return 1;
*key_part++= first_key_part[j];
*rec_per_key++= 0;
keyinfo->ext_key_parts++;
@@ -917,13 +920,17 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
share->ext_key_parts++;
share->ext_key_parts+= keyinfo->ext_key_parts;
}
keynames->str= (char*) key_part;
keynames->length= strnmov(keynames->str, (char *) strpos,
frm_image_end - strpos) - keynames->str;
strpos+= keynames->length;
if (*strpos++) // key names are \0-terminated
if (key_part > key_part_end)
return 1;
keynames->length++; // Include '\0', to make fix_type_pointers() happy.
size_t max_keyname_len= MY_MIN(len, (uint)(frm_image_end - strpos));
keynames->str= (char*) key_part;
keynames->length= strnmov(keynames->str, (char *)strpos,
max_keyname_len) - keynames->str;
if (keynames->length >= max_keyname_len)
return 1; // meaning key name was not \0-terminated
keynames->length++; // include '\0', to make fix_type_pointers() happy.
strpos+= keynames->length;
len-= (uint)keynames->length;
//reading index comments
for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++)
@@ -935,11 +942,13 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
keyinfo->comment.length= uint2korr(strpos);
strpos+= 2;
if (strpos + keyinfo->comment.length >= frm_image_end)
if (strpos + keyinfo->comment.length >= frm_image_end ||
keyinfo->comment.length > len)
return 1;
keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos,
keyinfo->comment.length);
strpos+= keyinfo->comment.length;
len-= (uint)keyinfo->comment.length;
}
DBUG_ASSERT(MY_TEST(keyinfo->flags & HA_USES_COMMENT) ==
(keyinfo->comment.length > 0));
@@ -1058,7 +1067,8 @@ static void mysql57_calculate_null_position(TABLE_SHARE *share,
uchar **null_pos,
uint *null_bit_pos,
const uchar *strpos,
const uchar *vcol_screen_pos)
const uchar *vcol_screen_pos,
const uchar *vcol_screen_end)
{
uint field_pack_length= 17;
@@ -1069,6 +1079,8 @@ static void mysql57_calculate_null_position(TABLE_SHARE *share,
if ((strpos[10] & MYSQL57_GENERATED_FIELD))
{
if (vcol_screen_pos + MYSQL57_GCOL_HEADER_SIZE >= vcol_screen_end)
return;
/* Skip virtual (not stored) generated field */
bool stored_in_db= vcol_screen_pos[3];
vcol_screen_pos+= (uint2korr(vcol_screen_pos + 1) +
@@ -1788,6 +1800,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
const uchar *forminfo;
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos);
uchar *data_start, *data_end;
uchar *first_stored= 0, *first_virtual= 0, *next_stored= 0, *next_virtual= 0;
const uchar *disk_buff, *strpos;
ulong pos, record_offset;
ulong rec_buff_length;
@@ -1795,13 +1809,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
KEY *keyinfo;
KEY_PART_INFO *key_part= NULL;
Field **field_ptr, *reg_field;
const char **interval_array;
const char **interval_array, **interval_array_end;
uint *typelib_value_lengths= NULL;
enum legacy_db_type legacy_db_type;
my_bitmap_map *bitmaps;
bool null_bits_are_used;
uint vcol_screen_length;
uchar *vcol_screen_pos;
uchar *vcol_screen_pos, *vcol_screen_end;
LEX_CUSTRING options;
LEX_CSTRING se_name= empty_clex_str;
KEY first_keyinfo;
@@ -1811,7 +1825,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bool vers_can_native= false, frm_created= 0;
Field_data_type_info_array field_data_type_info_array;
MEM_ROOT *old_root= thd->mem_root;
Virtual_column_info **table_check_constraints;
Virtual_column_info **table_check_constr;
bool *interval_unescaped= NULL;
extra2_fields extra2;
bool extra_index_flags_present= FALSE;
@@ -1852,6 +1866,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/* Length of the MariaDB extra2 segment in the form file. */
len = uint2korr(frm_image+4);
if (frm_length < FRM_HEADER_SIZE + len ||
!(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len)))
goto err;
forminfo= frm_image + pos;
if (forminfo + FRM_FORMINFO_SIZE >= frm_image_end)
goto err;
if (read_extra2(frm_image, len, &extra2))
goto err;
@@ -1871,17 +1893,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!share->default_part_plugin)
goto err;
}
#endif
if (frm_length < FRM_HEADER_SIZE + len ||
!(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len)))
goto err;
forminfo= frm_image + pos;
if (forminfo + FRM_FORMINFO_SIZE >= frm_image_end)
goto err;
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (frm_image[61] && !share->default_part_plugin)
{
enum legacy_db_type db_type= (enum legacy_db_type) (uint) frm_image[61];
@@ -1973,6 +1985,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->keys_in_use.init(keys);
ext_key_parts= key_parts;
if ((key_parts && !keys) || key_parts < keys)
goto err;
if (extra2.index_flags.str && extra2.index_flags.length != keys)
goto err;
@@ -2129,12 +2144,10 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
//reading long table comment
if (next_chunk + 2 > buff_end)
{
DBUG_PRINT("error",
("long table comment is not defined in .frm"));
goto err;
}
share->comment.length = uint2korr(next_chunk);
if (next_chunk + 2 + share->comment.length > buff_end)
goto err;
if (! (share->comment.str= strmake_root(&share->mem_root,
(char*)next_chunk + 2, share->comment.length)))
{
@@ -2143,17 +2156,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
next_chunk+= 2 + share->comment.length;
}
DBUG_ASSERT(next_chunk <= buff_end);
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS_legacy)
{
if (options.str)
goto err;
if (next_chunk + 4 > buff_end)
goto err;
options.length= uint4korr(next_chunk);
options.str= next_chunk + 4;
next_chunk+= options.length + 4;
if (next_chunk > buff_end)
goto err;
}
DBUG_ASSERT(next_chunk <= buff_end);
}
else
{
@@ -2165,7 +2179,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->key_block_size= uint2korr(frm_image+62);
keyinfo= share->key_info;
if (extra2.index_flags.str)
extra_index_flags_present= TRUE;
@@ -2210,6 +2223,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
disk_buff= frm_image + pos + FRM_FORMINFO_SIZE;
share->fields= uint2korr(forminfo+258);
if (share->fields <= 0 || share->fields > MAX_FIELDS)
goto err;
if (extra2.field_flags.str && extra2.field_flags.length != share->fields)
goto err;
pos= uint2korr(forminfo+260); /* Length of all screens */
@@ -2231,6 +2246,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->comment.length);
}
if (hash_fields > share->fields || share->null_fields > share->fields)
goto err;
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, keys,n_length,int_length, com_length, vcol_screen_length));
/*
@@ -2255,12 +2273,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
&share->field, (uint)(share->fields+1)*sizeof(Field*),
&share->intervals, (uint)interval_count*sizeof(TYPELIB),
&share->check_constraints, (uint) share->table_check_constraints * sizeof(Virtual_column_info*),
/*
This looks wrong: shouldn't it be (+2+interval_count)
instread of (+3) ?
*/
&interval_array, (uint) (share->fields+interval_parts+ keys+3)*sizeof(char *),
&typelib_value_lengths, total_typelib_value_count * sizeof(uint *),
&interval_array, (uint)total_typelib_value_count * sizeof(char *),
&typelib_value_lengths, (uint)total_typelib_value_count * sizeof(uint),
&names, (uint) (n_length+int_length),
&comment_pos, (uint) com_length,
&vcol_screen_pos, vcol_screen_length,
@@ -2276,10 +2290,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
field_ptr= share->field;
table_check_constraints= share->check_constraints;
interval_array_end= interval_array + total_typelib_value_count;
table_check_constr= share->check_constraints;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length+
vcol_screen_length));
if (disk_buff + read_length > frm_image_end)
goto err;
strpos= disk_buff+pos;
if (!interval_count)
@@ -2293,21 +2310,24 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
com_length);
memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length,
vcol_screen_length);
vcol_screen_end= vcol_screen_pos + vcol_screen_length;
if (fix_type_pointers(&interval_array, &typelib_value_lengths,
&share->fieldnames, 1, names, n_length) ||
if (fix_type_pointers(&interval_array, interval_array_end,
&typelib_value_lengths, &share->fieldnames, 1, names,
n_length) ||
share->fieldnames.count != share->fields)
goto err;
if (fix_type_pointers(&interval_array, &typelib_value_lengths,
share->intervals, interval_count,
names + n_length, int_length))
if (fix_type_pointers(&interval_array, interval_array_end,
&typelib_value_lengths, share->intervals,
interval_count, names + n_length, int_length))
goto err;
if (keynames.length &&
(fix_type_pointers(&interval_array, &typelib_value_lengths,
&share->keynames, 1, keynames.str, keynames.length) ||
share->keynames.count != keys))
if ((keynames.length &&
fix_type_pointers(&interval_array, interval_array_end,
&typelib_value_lengths, &share->keynames, 1,
keynames.str, keynames.length)) ||
share->keynames.count != keys)
goto err;
/* Allocate handler */
@@ -2331,14 +2351,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
*/
share->null_bytes= (share->null_fields + null_bit_pos + 7) / 8;
}
#ifndef WE_WANT_TO_SUPPORT_VERY_OLD_FRM_FILES
else
{
share->null_bytes= (share->null_fields+7)/8;
null_flags= null_pos= record + 1 + share->reclength - share->null_bytes;
null_bit_pos= 0;
}
#endif
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
if (use_hash)
@@ -2359,7 +2377,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
mysql57_vcol_null_bit_pos= null_bit_pos;
mysql57_calculate_null_position(share, &mysql57_vcol_null_pos,
&mysql57_vcol_null_bit_pos,
strpos, vcol_screen_pos);
strpos, vcol_screen_pos, vcol_screen_end);
}
/* Set system versioning information. */
@@ -2408,20 +2426,22 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (extra2.without_overlaps.str)
{
if (extra2.application_period.str == NULL)
if (extra2.application_period.str == NULL ||
extra2.without_overlaps.length < frm_keyno_size)
goto err;
const uchar *key_pos= extra2.without_overlaps.str;
period.unique_keys= read_frm_keyno(key_pos);
if (period.unique_keys > keys ||
extra2.without_overlaps.length != (period.unique_keys+1)*frm_keyno_size)
goto err;
for (uint k= 0; k < period.unique_keys; k++)
{
key_pos+= frm_keyno_size;
uint key_nr= read_frm_keyno(key_pos);
if (key_nr >= keys)
goto err;
key_info[key_nr].without_overlaps= true;
}
if ((period.unique_keys + 1) * frm_keyno_size
!= extra2.without_overlaps.length)
goto err;
}
if (extra2.field_data_type_info.length &&
@@ -2455,6 +2475,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
comment.str= (char*) comment_pos;
comment.length= comment_length;
comment_pos+= comment_length;
if (com_length < comment_length)
goto err;
com_length-= comment_length;
}
if (strpos[13] == MYSQL_TYPE_VIRTUAL &&
@@ -2467,7 +2490,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
*/
uint vcol_info_length= (uint) strpos[12];
if (!vcol_info_length) // Expect non-null expression
if (vcol_info_length < 4 ||
vcol_screen_pos + vcol_info_length > vcol_screen_end)
goto err;
attr.frm_unpack_basic(strpos);
@@ -2569,15 +2593,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
byte 4 = stored_in_db
byte 5.. = expr
*/
if ((uint)(vcol_screen_pos)[0] != 1)
if (vcol_screen_pos + MYSQL57_GCOL_HEADER_SIZE > vcol_screen_end ||
vcol_screen_pos[0] != 1)
goto err;
vcol_info= new (&share->mem_root) Virtual_column_info();
uint vcol_info_length= uint2korr(vcol_screen_pos + 1);
if (!vcol_info_length) // Expect non-empty expression
goto err;
vcol_info->stored_in_db= vcol_screen_pos[3];
vcol_info->utf8= 0;
vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;;
if (!vcol_info_length || // Expect non-empty expression
vcol_screen_pos > vcol_screen_end)
goto err;
share->virtual_fields++;
}
}
@@ -2634,6 +2660,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
attr.pack_flag&= ~FIELDFLAG_LONG_DECIMAL;
}
if (interval_nr > interval_count)
goto err;
if (interval_nr && (attr.charset->mbminlen > 1 ||
(attr.pack_flag & FIELDFLAG_FRM_HEX_ENCODED_TYPELIB)))
{
@@ -2735,6 +2764,52 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!reg_field) // Not supported field type
goto err;
/*
Verify that fields follow one another in the record without overlaps.
That is the next field starts where the previous field ended.
Virtual fields complicate the check. Stored fields follow each other in
the record order, virtual fields also follow each in the record order
but they're always at the end of the record, and can be interleaved
with normal fields in the table.
We detect this dynamically - as long as the next field starts where the
previous ended, assume they're normal fields. When the next field starts
with a gap - it's the first virtual (and the future virtual fields must
follow that field). If the next field starts before the previous - it
means the table started from virtual fields and this is the first stored
field. Either ("gap" or "before") can happen only once.
*/
if (!next_stored) // first field, i == 0
{
first_stored= reg_field->ptr;
next_stored= first_stored + reg_field->pack_length_in_rec();
}
else if (reg_field->ptr == next_stored) // next stored field
next_stored+= reg_field->pack_length_in_rec();
else if (reg_field->ptr > next_stored) // virtual
{
if (next_virtual)
{
if (reg_field->ptr != next_virtual)
goto err;
next_virtual+= reg_field->pack_length_in_rec();
}
else
{
first_virtual= reg_field->ptr;
next_virtual= first_virtual + reg_field->pack_length_in_rec();
}
}
else // reg_field < next_stored. can happen if the i=0 field was virtual
{
if (next_virtual)
goto err;
first_virtual= first_stored;
next_virtual= next_stored;
first_stored= reg_field->ptr;
next_stored= first_stored + reg_field->pack_length_in_rec();
}
if (attr.unireg_check == Field::TIMESTAMP_DNUN_FIELD ||
attr.unireg_check == Field::TIMESTAMP_DN_FIELD)
{
@@ -2818,7 +2893,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/* We want to store the value for the last bits */
swap_variables(uchar*, null_pos, mysql57_vcol_null_pos);
swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
DBUG_ASSERT((null_pos + (null_bit_pos + 7) / 8) <= share->field[0]->ptr);
if ((null_pos + (null_bit_pos + 7) / 8) > share->field[0]->ptr)
goto err;
}
/* Fix key->name and key_part->field */
@@ -2846,7 +2922,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
hash_keypart->fieldnr= hash_field_used_no + 1;
hash_field= share->field[hash_field_used_no];
hash_field->flags|= LONG_UNIQUE_HASH_FIELD;//Used in parse_vcol_defs
DBUG_ASSERT(hash_field->invisible == INVISIBLE_FULL);
if (hash_field->invisible != INVISIBLE_FULL ||
hash_field->pack_length() != HA_HASH_FIELD_LENGTH)
goto err;
keyinfo->flags|= HA_NOSAME;
share->virtual_fields++;
share->stored_fields--;
@@ -2875,7 +2953,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
key_part= keyinfo->key_part;
for (i=0 ; i < keyinfo->user_defined_key_parts ;i++)
{
DBUG_ASSERT(key_part[i].fieldnr > 0);
if (key_part[i].fieldnr <= 0 || key_part[i].fieldnr > share->fields)
goto err;
// Table field corresponding to the i'th key part.
Field *table_field= share->field[key_part[i].fieldnr - 1];
@@ -2943,6 +3022,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
for (i= 0; i < first_keyinfo.user_defined_key_parts; i++)
{
uint fieldnr= keyinfo[0].key_part[i].fieldnr;
if (fieldnr <= 0 || fieldnr > share->fields)
goto err;
if (share->field[fieldnr-1]->key_length() !=
keyinfo[0].key_part[i].length)
{
@@ -2990,6 +3071,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
uint length_bytes= 0;
uint fieldnr= keyinfo->key_part[i].fieldnr;
if (fieldnr <= 0 || fieldnr > share->fields)
goto err;
field= share->field[fieldnr-1];
if (field->null_ptr)
@@ -3067,7 +3150,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->default_values,
(uint) key_part->offset,
(uint) key_part->length);
if (!key_part->fieldnr)
if (key_part->fieldnr <= 0 || key_part->fieldnr > share->fields)
goto err;
field= key_part->field= share->field[key_part->fieldnr-1];
@@ -3219,7 +3302,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
else
share->primary_key= MAX_KEY;
if (new_field_pack_flag <= 1)
if (new_field_pack_flag <= 1 && share->null_fields)
{
/* Old file format with default as not null */
uint null_length= (share->null_fields+7)/8;
@@ -3232,8 +3315,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
/* Handle virtual expressions */
if (vcol_screen_length && share->frm_version >= FRM_VER_EXPRESSSIONS)
{
uchar *vcol_screen_end= vcol_screen_pos + vcol_screen_length;
/* Skip header */
vcol_screen_pos+= FRM_VCOL_NEW_BASE_SIZE;
share->vcol_defs.str+= FRM_VCOL_NEW_BASE_SIZE;
@@ -3246,6 +3327,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
while (vcol_screen_pos < vcol_screen_end)
{
Virtual_column_info *vcol_info;
if (vcol_screen_end - vcol_screen_pos < FRM_VCOL_NEW_HEADER_SIZE)
goto err;
uint type= (uint) vcol_screen_pos[0];
uint field_nr= uint2korr(vcol_screen_pos+1);
uint expr_length= uint2korr(vcol_screen_pos+3);
@@ -3258,17 +3342,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (field_nr != UINT_MAX16)
{
DBUG_ASSERT(field_nr < share->fields);
if (field_nr >= share->fields)
goto err;
reg_field= share->field[field_nr];
}
else
{
reg_field= 0;
DBUG_ASSERT(name_length);
if (!name_length || type != VCOL_CHECK_TABLE)
goto err;
}
vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE;
vcol_info->set_vcol_type((enum_vcol_info_type) type);
if ((uint)(vcol_screen_end-vcol_screen_pos) < name_length+expr_length)
goto err;
if (name_length)
{
vcol_info->name.str= strmake_root(&share->mem_root,
@@ -3282,7 +3370,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
switch (type) {
case VCOL_GENERATED_VIRTUAL:
{
uint recpos;
if (reg_field->vcol_info)
goto err;
reg_field->vcol_info= vcol_info;
share->virtual_fields++;
share->stored_fields--;
@@ -3291,37 +3380,43 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (reg_field->flags & PART_KEY_FLAG)
vcol_info->set_vcol_type(VCOL_GENERATED_VIRTUAL_INDEXED);
/* Correct stored_rec_length as non stored fields are last */
recpos= (uint) (reg_field->ptr - record);
uint recpos= (uint) (reg_field->ptr - record);
if (share->stored_rec_length >= recpos)
share->stored_rec_length= recpos-1;
break;
}
case VCOL_GENERATED_STORED:
vcol_info->stored_in_db= 1;
DBUG_ASSERT(!reg_field->vcol_info);
if (reg_field->vcol_info)
goto err;
reg_field->vcol_info= vcol_info;
share->virtual_fields++;
break;
case VCOL_DEFAULT:
vcol_info->stored_in_db= 1;
DBUG_ASSERT(!reg_field->default_value);
if (reg_field->default_value)
goto err;
reg_field->default_value= vcol_info;
share->default_expressions++;
break;
case VCOL_CHECK_FIELD:
DBUG_ASSERT(!reg_field->check_constraint);
if (reg_field->check_constraint)
goto err;
reg_field->check_constraint= vcol_info;
share->field_check_constraints++;
break;
case VCOL_CHECK_TABLE:
*(table_check_constraints++)= vcol_info;
if ((uint)(table_check_constr - share->check_constraints) >=
share->table_check_constraints)
goto err;
*(table_check_constr++)= vcol_info;
break;
}
}
}
DBUG_ASSERT((uint) (table_check_constraints - share->check_constraints) ==
(uint) (share->table_check_constraints -
share->field_check_constraints));
if (table_check_constr - share->check_constraints !=
(int)share->table_check_constraints - (int)share->field_check_constraints)
goto err;
if (options.str)
{
@@ -3374,6 +3469,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->can_cmp_whole_record= (share->blob_fields == 0 &&
share->varchar_fields == 0);
data_start= share->default_values;
data_end= data_start + share->reclength;
if (share->null_field_first)
data_start+= share->null_bytes;
else
data_end-= share->null_bytes;
if (data_end < data_start ||
(first_virtual && first_virtual != next_stored) ||
first_stored < data_start ||
(next_virtual ? next_virtual : next_stored) > data_end)
goto err;
share->column_bitmap_size= bitmap_buffer_size(share->fields);
bitmap_count= 1;
@@ -4823,19 +4931,20 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
} /* open_table_error */
/*
** fix a str_type to a array type
** typeparts separated with some char. differents types are separated
** with a '\0'
*/
/*
fix a str_type to a array type
typeparts separated with some char. differents types are separated
with a '\0'
*/
static bool
fix_type_pointers(const char ***typelib_value_names,
fix_type_pointers(const char ***typelib_value_names, const char **names_end,
uint **typelib_value_lengths,
TYPELIB *point_to_type, uint types,
char *ptr, size_t length)
{
const char *end= ptr + length;
names_end--; // simplify the check below, reserve place for 0 at the end
while (types--)
{
@@ -4867,7 +4976,7 @@ fix_type_pointers(const char ***typelib_value_names,
{
// Now scan the next value+sep pair
char *vend= (char*) memchr(ptr, sep, end - ptr);
if (!vend)
if (!vend || *typelib_value_names >= names_end)
return true; // Bad format
*((*typelib_value_names)++)= ptr;
*((*typelib_value_lengths)++)= (uint) (vend - ptr);

View File

@@ -947,14 +947,21 @@ uint32 ha_archive::max_row_length(const uchar *record)
unsigned int ha_archive::pack_row(const uchar *record, azio_stream *writer)
{
uchar *ptr;
uint32 max_len;
my_ptrdiff_t const rec_offset= record - table->record[0];
DBUG_ENTER("ha_archive::pack_row");
if (fix_rec_buff(max_row_length(record)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
max_len= max_row_length(record);
if (writer->version == 1)
{
if (fix_rec_buff(max_len))
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
DBUG_RETURN(pack_row_v1(record));
}
if (fix_rec_buff(max_len + ARCHIVE_ROW_HEADER_SIZE))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
/* Copy null bits */
memcpy(record_buffer->buffer+ARCHIVE_ROW_HEADER_SIZE,
@@ -1582,6 +1589,46 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
while (!(rc= get_row(&archive, table->record[0])))
{
/*
If the source is version 3, unpack_row() stores blob data in
record_buffer. Since pack_row() also uses record_buffer for
packing, we must copy blob data out first to avoid corruption.
Version 2 already stores blobs in the 'buffer' member.
*/
if (archive.version >= 3)
{
uint *blob_ptr, *blob_end;
size_t total_blob_length= 0;
for (blob_ptr= table->s->blob_field,
blob_end= blob_ptr + table->s->blob_fields;
blob_ptr != blob_end; blob_ptr++)
{
Field_blob *blob= (Field_blob*) table->field[*blob_ptr];
if (!blob->is_null())
total_blob_length+= blob->get_length();
}
if (buffer.alloc(total_blob_length))
{
rc= HA_ERR_OUT_OF_MEM;
break;
}
char *pos= (char*) buffer.ptr();
for (blob_ptr= table->s->blob_field,
blob_end= blob_ptr + table->s->blob_fields;
blob_ptr != blob_end; blob_ptr++)
{
Field_blob *blob= (Field_blob*) table->field[*blob_ptr];
if (blob->is_null())
continue;
uint32 length= blob->get_length();
if (length)
{
memcpy(pos, blob->get_ptr(), length);
blob->set_ptr(length, (uchar*) pos);
pos+= length;
}
}
}
real_write_row(table->record[0], &writer);
/*
Long term it should be possible to optimize this so that
@@ -1601,6 +1648,7 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
tmp_restore_column_map(&table->read_set, org_bitmap);
share->rows_recorded= (ha_rows)writer.rows;
buffer.free();
}
DBUG_PRINT("info", ("recovered %llu archive rows",

View File

@@ -29,14 +29,14 @@ SET(CONNECT_SOURCES
ha_connect.cc connect.cc user_connect.cc mycat.cc
fmdlex.c osutil.c rcmsg.c rcmsg.h
array.cpp blkfil.cpp colblk.cpp csort.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp filter.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp filechk.cpp filter.cpp
json.cpp jsonudf.cpp maputil.cpp myconn.cpp myutil.cpp plgdbutl.cpp plugutil.cpp
reldef.cpp tabcol.cpp tabdos.cpp tabext.cpp tabfix.cpp tabfmt.cpp tabjson.cpp
table.cpp tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp
tabutil.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp
array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h
engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h filechk.h
filter.h global.h ha_connect.h inihandl.h json.h jsonudf.h maputil.h msgid.h
mycat.h myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h
resource.h tabcol.h tabdos.h tabext.h tabfix.h tabfmt.h tabjson.h tabmul.h

View File

@@ -16,6 +16,7 @@
#include <cassert>
#include "bsonudf.h"
#include "filechk.h"
#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
@@ -4478,6 +4479,16 @@ char *bson_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
PlugSubSet(g->Sarea, g->Sarea_Size);
fn = MakePSZ(g, args, 0);
if (!fn) {
PUSH_WARNING("Missing file name");
*is_null = 1;
return NULL;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*is_null = 1;
return NULL;
}
if (args->arg_count > 1) {
int pretty = 3, pty = 3;
size_t len;
@@ -4629,7 +4640,10 @@ char *bfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endswitch arg_type
if (fn) {
if (!bnx.Serialize(g, jvp, fn, pretty))
if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
fn = NULL;
} else if (!bnx.Serialize(g, jvp, fn, pretty))
PUSH_WARNING(g->Message);
} else
PUSH_WARNING("Missing file name");
@@ -6065,6 +6079,16 @@ char *bbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
fn = MakePSZ(g, args, 0);
if (!fn) {
PUSH_WARNING("Missing file name");
*error = 1;
goto fin;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*error = 1;
goto fin;
}
for (unsigned int i = 1; i < args->arg_count; i++)
if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
pretty = (int) * (longlong*)args->args[i];

View File

@@ -246,7 +246,7 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, PTOS topt, bool info)
bool bad, mul;
PCSZ target, pwd;
DBFHEADER mainhead, *hp = NULL;
DESCRIPTOR thisfield, *tfp;
DESCRIPTOR thisfield, *tfp= &thisfield;
FILE *infile = NULL;
UNZIPUTL *zutp = NULL;
PQRYRES qrp;
@@ -308,7 +308,6 @@ PQRYRES DBFColumns(PGLOBAL g, PCSZ dp, PCSZ fn, PTOS topt, bool info)
return NULL;
} // endif dbfhead
tfp = &thisfield;
} // endif zipped
/************************************************************************/

View File

@@ -0,0 +1,41 @@
/* Copyright (C) MariaDB Corporation Ab
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 Street, Fifth Floor, Boston, MA 02110-1335 USA
*/
#include <my_global.h>
#include <mysqld.h>
#include <sql_parse.h>
#include "filechk.h"
/*
@return true if the current user may access path.
*/
bool connect_can_access_file(const char *path)
{
char real_path[FN_REFLEN];
if (!path)
return false; /* callers must check for NULL before calling */
if (check_global_access(current_thd, FILE_ACL, true))
return false;
/* MY_SAFE_PATH returns NULL on overflow; fail closed rather than validate a truncated path */
if (!fn_format(real_path, path, mysql_real_data_home, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | MY_SAFE_PATH))
return false;
return is_secure_file_path(real_path);
}

20
storage/connect/filechk.h Normal file
View File

@@ -0,0 +1,20 @@
/* Copyright (C) MariaDB Corporation Ab
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 Street, Fifth Floor, Boston, MA 02110-1335 USA
*/
/* File access checks for CONNECT file UDFs */
#pragma once
bool connect_can_access_file(const char *path);

View File

@@ -15,6 +15,7 @@
#include <m_string.h>
#include "jsonudf.h"
#include "filechk.h"
#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
@@ -4562,6 +4563,16 @@ char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
PlugSubSet(g->Sarea, g->Sarea_Size);
fn = MakePSZ(g, args, 0);
if (!fn) {
PUSH_WARNING("Missing file name");
*is_null = 1;
return NULL;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*is_null = 1;
return NULL;
}
if (args->arg_count > 1) {
int pretty = 3, pty = 3;
size_t len;
@@ -4715,7 +4726,10 @@ char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endswitch arg_type
if (fn) {
if (!Serialize(g, jvp->GetJson(), fn, pretty))
if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
fn = NULL;
} else if (!Serialize(g, jvp->GetJson(), fn, pretty))
PUSH_WARNING(g->Message);
} else
PUSH_WARNING("Missing file name");
@@ -5767,8 +5781,8 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
if (args->arg_count < 1 || args->arg_count > 4) {
strcpy(message, "This function only accepts 1 to 4 arguments");
return true;
} else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) {
strcpy(message, "First argument must be a constant string (file name)");
} else if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "First argument must be a string (file name)");
return true;
} // endifs
@@ -5786,7 +5800,10 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
initid->maybe_null = 1;
CalcLen(args, false, reslen, memlen);
fl = GetFileLength(args->args[0]);
if (args->args[0])
fl = GetFileLength(args->args[0]);
else
fl = 100; // What can be done here?
reslen += fl;
more += fl * M;
//memlen += more;
@@ -5811,6 +5828,16 @@ char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
g->Xchk = NULL;
fn = MakePSZ(g, args, 0);
if (!fn) {
PUSH_WARNING("Missing file name");
*error = 1;
goto fin;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*error = 1;
goto fin;
}
for (unsigned int i = 1; i < args->arg_count; i++)
if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
pretty = (int) * (longlong*)args->args[i];

View File

@@ -5,11 +5,3 @@ drop table mdev9949;
Warnings:
Warning 1017 Can't find file: 'DATADIR/test/mdev9949.dos' (errno: 2 "No such file or directory")
drop table t1;
select @@secure_file_priv 'must be NULL';
must be NULL
NULL
create table t1 (a char(16)) engine=myisam;
insert into t1 values('Hello World!');
create table t2 engine=connect file_name='foo/bar.txt' as select * from t1;
ERROR HY000: Got error 174 'Open(a+b) error 2 on foo/bar.txt: No such file or directory' from CONNECT
drop table t1;

View File

@@ -129,3 +129,13 @@ Sam Chicago 1979-11-22 2007-10-10
James Dallas 1992-05-13 2009-12-14
Bill Boston 1986-09-11 2008-02-10
DROP TABLE t1;
#
# MDEV-40637 CONNECT crashes on double(255,50) in DOS table
#
create table t (x double(255,30)) engine=connect table_type='dos' file_name='t.dos';
insert into t values (1e50);
select * from t;
x
1000000000000000000000000000000.000000000000000000000000000000
drop table t;
# End of 10.6 tests

View File

@@ -0,0 +1,156 @@
#
# Test FILE privilege and secure_file_priv enforcement for JSON/BSON file UDFs
#
# Privileged user: path inside secure_file_priv MYSQL_TMP_DIR
select json_file('MYSQL_TMP_DIR/bib0.json', 0) IS NOT NULL AS ok;
ok
1
select bson_file('MYSQL_TMP_DIR/biblio.json', 0) IS NOT NULL AS ok;
ok
1
Warnings:
Warning 1105 File pretty format doesn't match the specified pretty value
select jfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NOT NULL AS ok;
ok
1
select bfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NOT NULL AS ok;
ok
1
select jbin_file('MYSQL_TMP_DIR/bib0.json') IS NOT NULL AS ok;
ok
1
select bbin_file('MYSQL_TMP_DIR/biblio.json') IS NOT NULL AS ok;
ok
1
# Privileged user: path outside secure_file_priv
select json_file('DATADIR/bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('DATADIR/biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jfile_make('{"test":1}', 'DATADIR/out.json', 0) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bfile_make('{"test":1}', 'DATADIR/out.json', 0) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jbin_file('DATADIR/bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bbin_file('DATADIR/biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# NULL argument: no "access denied", UDF reports its own error
select json_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bson_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select jfile_make('{"test":1}', NULL, 0) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bfile_make('{"test":1}', NULL, 0) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select jbin_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bbin_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
# Privileged user: relative path resolving outside secure_file_priv
select json_file('../../bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('../../biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# Overlong filename: MY_SAFE_PATH rejects it, fail closed
select json_file(concat('MYSQL_TMP_DIR/', repeat('x', 512))) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file(concat('MYSQL_TMP_DIR/', repeat('x', 512))) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# Non-constant filename expression (args->args[0] is NULL at init time)
set @fname = 'MYSQL_TMP_DIR/bib0.json';
select jbin_file(@fname) IS NOT NULL AS ok;
ok
1
set @fname = 'MYSQL_TMP_DIR/biblio.json';
select bbin_file(@fname) IS NOT NULL AS ok;
ok
1
# User without FILE privilege
create user unprivileged@localhost;
grant select ON *.* TO unprivileged@localhost;
connect unprivileged,localhost,unprivileged,,;
connection unprivileged;
# inside secure_file_priv: denied without FILE privilege
select json_file('MYSQL_TMP_DIR/bib0.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('MYSQL_TMP_DIR/biblio.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jbin_file('MYSQL_TMP_DIR/bib0.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bbin_file('MYSQL_TMP_DIR/biblio.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
disconnect unprivileged;
connection default;
drop user unprivileged@localhost;

View File

@@ -1 +0,0 @@
--secure-file-priv=""

Some files were not shown because too many files have changed in this diff Show More