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

allocate Sql_path in one memory chunk, not one per schema

because it's always allocated and freed as a whole, there is
no operation "replace one name in the middle of the path"

this needed a reworked parser to create the whole list
of names, not append one name at a time.

Fixed a bug where Sql_path::from_text() pretended it can take
a string in any charset, but was always implicitly
assuming it's in my_charset_utf8mb3_general_ci.
This commit is contained in:
Sergei Golubchik
2025-11-27 00:29:35 +01:00
parent 7325f948e8
commit 69f401bdb6
7 changed files with 177 additions and 273 deletions

View File

@@ -425,63 +425,63 @@ SELECT @@global.path;
@@global.path
`.`,`sys`
SET PATH '`';
ERROR 42000: Variable 'path' can't be set to the value of '`'
ERROR 42000: Variable 'PATH' can't be set to the value of '`'
SET PATH '"';
ERROR 42000: Variable 'path' can't be set to the value of '"'
ERROR 42000: Variable 'PATH' can't be set to the value of '"'
SET PATH '`a';
ERROR 42000: Variable 'path' can't be set to the value of '`a'
ERROR 42000: Variable 'PATH' can't be set to the value of '`a'
SET PATH '"a';
ERROR 42000: Variable 'path' can't be set to the value of '"a'
ERROR 42000: Variable 'PATH' can't be set to the value of '"a'
SET PATH 'a`';
ERROR 42000: Variable 'path' can't be set to the value of 'a`'
ERROR 42000: Variable 'PATH' can't be set to the value of 'a`'
SET PATH 'a"';
ERROR 42000: Variable 'path' can't be set to the value of 'a"'
ERROR 42000: Variable 'PATH' can't be set to the value of 'a"'
SET PATH '"d"';
ERROR 42000: Variable 'path' can't be set to the value of '"d"'
ERROR 42000: Variable 'PATH' can't be set to the value of '"d"'
SET PATH '`aaa';
ERROR 42000: Variable 'path' can't be set to the value of '`aaa'
ERROR 42000: Variable 'PATH' can't be set to the value of '`aaa'
SET PATH 'aaa`';
ERROR 42000: Variable 'path' can't be set to the value of 'aaa`'
ERROR 42000: Variable 'PATH' can't be set to the value of 'aaa`'
SET PATH '"aaa';
ERROR 42000: Variable 'path' can't be set to the value of '"aaa'
ERROR 42000: Variable 'PATH' can't be set to the value of '"aaa'
SET PATH 'aaa"';
ERROR 42000: Variable 'path' can't be set to the value of 'aaa"'
ERROR 42000: Variable 'PATH' can't be set to the value of 'aaa"'
SET PATH '`aaa"';
ERROR 42000: Variable 'path' can't be set to the value of '`aaa"'
ERROR 42000: Variable 'PATH' can't be set to the value of '`aaa"'
SET PATH '"aaa`';
ERROR 42000: Variable 'path' can't be set to the value of '"aaa`'
ERROR 42000: Variable 'PATH' can't be set to the value of '"aaa`'
SET PATH '"ggg,hhh';
ERROR 42000: Variable 'path' can't be set to the value of '"ggg,hhh'
ERROR 42000: Variable 'PATH' can't be set to the value of '"ggg,hhh'
SET PATH 'ggg",hhh';
ERROR 42000: Variable 'path' can't be set to the value of 'ggg",hhh'
ERROR 42000: Variable 'PATH' can't be set to the value of 'ggg",hhh'
SET PATH '"ggg",hhh';
ERROR 42000: Variable 'path' can't be set to the value of '"ggg",hhh'
ERROR 42000: Variable 'PATH' can't be set to the value of '"ggg",hhh'
SET PATH 'hhh,`iii`,jjj,"kkk",lll';
ERROR 42000: Variable 'path' can't be set to the value of 'hhh,`iii`,jjj,"kkk",lll'
ERROR 42000: Variable 'PATH' can't be set to the value of 'hhh,`iii`,jjj,"kkk",lll'
SET PATH 'hello,world,deadpool,wolverine,aaa,deadpool,ccc';
ERROR 42000: Variable 'path' can't be set to the value of 'deadpool'
ERROR 42000: Variable 'PATH' can't be set to the value of 'hello,world,deadpool,wolverine,aaa,deadpool,ccc'
SET PATH '`,Ɣ';
ERROR 42000: Variable 'path' can't be set to the value of '`,Ɣ'
ERROR 42000: Variable 'PATH' can't be set to the value of '`,Ɣ'
SET PATH '",Ɣ';
ERROR 42000: Variable 'path' can't be set to the value of '",Ɣ'
ERROR 42000: Variable 'PATH' can't be set to the value of '",Ɣ'
SET PATH 'Ɣ,`';
ERROR 42000: Variable 'path' can't be set to the value of 'Ɣ,`'
ERROR 42000: Variable 'PATH' can't be set to the value of 'Ɣ,`'
SET PATH 'Ɣ,"';
ERROR 42000: Variable 'path' can't be set to the value of 'Ɣ,"'
ERROR 42000: Variable 'PATH' can't be set to the value of 'Ɣ,"'
SET PATH '`Œ';
ERROR 42000: Variable 'path' can't be set to the value of '`Œ'
ERROR 42000: Variable 'PATH' can't be set to the value of '`Œ'
SET PATH '"c';
ERROR 42000: Variable 'path' can't be set to the value of '"c'
ERROR 42000: Variable 'PATH' can't be set to the value of '"c'
SET PATH 'Œ`';
ERROR 42000: Variable 'path' can't be set to the value of 'Œ`'
ERROR 42000: Variable 'PATH' can't be set to the value of 'Œ`'
SET PATH 'Œ"';
ERROR 42000: Variable 'path' can't be set to the value of 'Œ"'
ERROR 42000: Variable 'PATH' can't be set to the value of 'Œ"'
SET PATH '`Œ"';
ERROR 42000: Variable 'path' can't be set to the value of '`Œ"'
ERROR 42000: Variable 'PATH' can't be set to the value of '`Œ"'
SET PATH '"Œ`';
ERROR 42000: Variable 'path' can't be set to the value of '"Œ`'
ERROR 42000: Variable 'PATH' can't be set to the value of '"Œ`'
SET PATH '"Ɣ"';
ERROR 42000: Variable 'path' can't be set to the value of '"Ɣ"'
ERROR 42000: Variable 'PATH' can't be set to the value of '"Ɣ"'
SELECT @@path;
@@path
`.`,`sys`
@@ -492,8 +492,10 @@ SET PATH ' ';
SET PATH ' ';
SET PATH ',';
SET PATH '``';
ERROR 42000: Incorrect database name ''
SET sql_mode=ANSI_QUOTES;
SET PATH '""';
ERROR 42000: Incorrect database name ''
SET sql_mode=DEFAULT;
#
# Escaping backticks
@@ -507,9 +509,9 @@ SELECT @@path;
@@path
`a```
SET PATH '``a`';
ERROR 42000: Variable 'path' can't be set to the value of '``a`'
ERROR 42000: Incorrect database name ''
SET PATH '`a``';
ERROR 42000: Variable 'path' can't be set to the value of '`a``'
ERROR 42000: Variable 'PATH' can't be set to the value of '`a``'
SET GLOBAL PATH=DEFAULT;
SET @@path=DEFAULT;
#
@@ -520,7 +522,7 @@ SELECT @@path;
@@path
`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`,`i`,`j`,`k`,`l`,`m`,`n`,`o`,`p`
SET PATH 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q';
ERROR HY000: Too long value for 'path'
ERROR HY000: Too long value for 'PATH'
SELECT @@path;
@@path
`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`,`i`,`j`,`k`,`l`,`m`,`n`,`o`,`p`

View File

@@ -324,9 +324,11 @@ SELECT @@path;
SET PATH ' ';
SET PATH ' ';
SET PATH ',';
SET PATH '``';
--Error ER_WRONG_DB_NAME
SET PATH '``';
SET sql_mode=ANSI_QUOTES;
--Error ER_WRONG_DB_NAME
SET PATH '""';
SET sql_mode=DEFAULT;
@@ -338,7 +340,7 @@ SELECT @@path;
SET PATH '`a```';
SELECT @@path;
--error ER_WRONG_VALUE_FOR_VAR
--error ER_WRONG_DB_NAME
SET PATH '``a`';
--error ER_WRONG_VALUE_FOR_VAR
SET PATH '`a``';

View File

@@ -766,7 +766,7 @@ Sp_handler::db_find_routine(THD *thd, const Database_qualified_name *name,
if (!table->field[MYSQL_PROC_FIELD_PATH]->val_str(&sql_path_str))
sql_path_str.set(STRING_WITH_LEN("CURRENT_SCHEMA"), system_charset_info);
sql_path.from_text(thd, sql_path_str.charset(), sql_path_str.to_lex_cstring());
sql_path.from_text(thd, sql_path_str.to_lex_cstring());
creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table);
trans_commited= 1;

View File

@@ -14,7 +14,7 @@ Sql_path::Sql_path() : m_count(0)
Sql_path& Sql_path::operator=(const Sql_path &rhs)
{
set(current_thd, rhs);
set(rhs);
return *this;
}
@@ -60,7 +60,7 @@ bool Sql_path::try_resolve_in_schema(THD *thd, const Lex_ident_db_normalized &sc
/*
[schema] '.' routine_name
*/
name->m_db= Lex_ident_db_normalized(thd->strmake(schema.str, schema.length),
name->m_db= Lex_ident_db_normalized(thd->strmake(schema.str, schema.length),
schema.length);
*resolved= true;
return false;
@@ -147,21 +147,21 @@ bool Sql_path::resolve(THD *thd, sp_head *caller, sp_name *name,
for (size_t i= 0; i < m_count; i++)
{
Lex_ident_db schema= m_schemas[i];
// Resolve CURRENT_SCHEMA if needed
schema= resolve_current_schema(thd, caller, schema);
if (!schema.str)
continue; // CURRENT_SCHEMA resolution failed, skip this entry
/*
Schemas are already normalized when added to the path, except for
Schemas are already normalized when added to the path, except for
CURRENT_SCHEMA which was resolved above. We can use them directly.
*/
const Lex_ident_db_normalized dbn(schema.str, schema.length);
if (try_resolve_in_schema(thd, dbn, name, sph, pkgname, &resolved))
return true;
if (resolved)
break;
}
@@ -172,11 +172,8 @@ bool Sql_path::resolve(THD *thd, sp_head *caller, sp_name *name,
void Sql_path::free()
{
for (size_t i= 0; i < m_count; i++)
{
my_free((void*)m_schemas[i].str);
}
if (m_count)
my_free(const_cast<char*>(m_schemas[0].str));
m_count= 0;
}
@@ -184,8 +181,9 @@ void Sql_path::free()
bool Sql_path::init()
{
free();
add_schema_direct(cur_schema.str, cur_schema.length);
char *buf= my_strndup(key_memory_Sys_var_charptr_value,
cur_schema.str, cur_schema.length, MYF(MY_WME));
m_schemas[m_count++]= {buf, cur_schema.length};
return false;
}
@@ -196,89 +194,40 @@ bool Sql_path::is_cur_schema(const LEX_CSTRING &schema) const
}
bool Sql_path::add_schema_direct(const char *schema_str, size_t schema_len)
bool Sql_path::add_schema(char **to)
{
if (unlikely(m_count >= array_elements(m_schemas)))
{
my_error(ER_VALUE_TOO_LONG , MYF(0), "path");
return true;
}
const Lex_ident_db &dbn= m_schemas[m_count];
m_schemas[m_count].length= *to - m_schemas[m_count].str;
*(*to)++= 0;
char *tmp= my_strndup(key_memory_Sys_var_charptr_value,
schema_str, schema_len,
MYF(MY_WME));
m_schemas[m_count++]= {tmp, schema_len};
return false;
}
// Validate
if (Lex_ident_db::check_name_with_error(dbn))
goto err;
bool Sql_path::add_schema(THD *thd, const char *schema_str, size_t schema_len)
{
DBUG_ASSERT(schema_str);
if (unlikely(m_count >= array_elements(m_schemas)))
{
my_error(ER_VALUE_TOO_LONG , MYF(0), "path");
return true;
}
if (is_cur_schema({schema_str, schema_len}))
return add_schema_direct(schema_str, schema_len);
// Step 1: Process backticks in-place using a local buffer
char local_buf[NAME_LEN * 3]; // Sufficient for any schema name
if (schema_len >= sizeof(local_buf))
{
my_error(ER_VALUE_TOO_LONG, MYF(0), "path");
return true;
}
size_t processed_len = 0;
for (size_t i = 0; i < schema_len; i++)
{
if (schema_str[i] == '`' && i + 1 < schema_len)
i++; // Skip first backtick of double backtick
local_buf[processed_len++] = schema_str[i];
}
local_buf[processed_len] = '\0';
// Step 2: Normalize the processed string
Lex_ident_db_normalized dbn = thd->to_ident_db_normalized_with_error(
Lex_cstring(local_buf, processed_len));
if (!dbn.str)
return true;
// Step 3: Check for duplicates
// Check for duplicates
for (size_t i = 0; i < m_count; i++)
{
if (unlikely(m_schemas[i].length == dbn.length &&
!memcmp(m_schemas[i].str, dbn.str, dbn.length)))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "path", dbn.str);
return true;
}
}
if (dbn.streq(m_schemas[i]))
goto err;
// Step 4: Make persistent copy and store
char *persistent_copy = my_strndup(key_memory_Sys_var_charptr_value,
dbn.str, dbn.length, MYF(MY_WME));
if (unlikely(!persistent_copy))
return true;
m_schemas[m_count++] = {persistent_copy, dbn.length};
m_count++;
return false;
err:
return true;
}
void Sql_path::set(THD *thd, const Sql_path &rhs)
void Sql_path::set(const Sql_path &rhs)
{
free();
for (size_t i= 0; i < rhs.m_count; i++)
if ((m_count= rhs.m_count))
{
if (add_schema(thd, rhs.m_schemas[i].str,
rhs.m_schemas[i].length))
break;
auto rbuf= rhs.m_schemas[0].str;
auto rend= rhs.m_schemas[m_count-1].str + rhs.m_schemas[m_count-1].length;
auto buf= (const char*)my_memdup(key_memory_Sys_var_charptr_value, rbuf,
rend - rbuf + 1, MYF(MY_WME));
for (size_t i= 0; i < rhs.m_count; i++)
m_schemas[i]= { rhs.m_schemas[i].str - rbuf + buf, rhs.m_schemas[i].length };
}
}
@@ -293,165 +242,127 @@ void Sql_path::set(Sql_path &&rhs)
}
bool Sql_path::from_text(THD *thd, CHARSET_INFO *cs, const LEX_CSTRING &text)
bool Sql_path::from_text(THD *thd, const LEX_CSTRING &text)
{
enum tokenize_state
{
START, QUOTED_TOKEN_DOUBLE='"', QUOTED_TOKEN_BACKTICK='`', UNQUOTED_TOKEN, END
} state= START;
const bool ansi_quotes= thd ? thd->variables.sql_mode & MODE_ANSI_QUOTES
: false;
CHARSET_INFO *cs= &my_charset_utf8mb3_general_ci; // as in make_ident_casedn()
DBUG_ASSERT(cs->cset->casedn_multiply(cs) == 1);
DBUG_ASSERT(text.str);
char *buf= (char*)my_malloc(key_memory_Sys_var_charptr_value,
text.length + 1, MYF(MY_WME));
if (!buf)
return true;
char *curr= buf, *to= buf, *end;
if (lower_case_table_names > 0)
end= buf + cs->cset->casedn(cs, text.str, text.length, buf, text.length);
else
{
memcpy(buf, text.str, text.length);
end= buf + text.length;
}
free();
enum class tokenize_state
while (curr < end)
{
START,
QUOTED_TOKEN_DOUBLE,
QUOTED_TOKEN_BACKTICK,
UNQUOTED_TOKEN,
END
} state= tokenize_state::START;
auto *curr= text.str;
auto *end= curr + text.length;
auto token_start= curr;
auto token_end= curr;
auto last_non_space= curr;
const bool ansi_quotes= thd ?
thd->variables.sql_mode & MODE_ANSI_QUOTES : false;
while (curr != end)
{
auto len = my_ismbchar(cs, curr, end - 1);
if (len)
{
if (state == tokenize_state::START)
{
state= tokenize_state::UNQUOTED_TOKEN;
token_start= curr;
}
curr += len;
last_non_space= curr - 1;
if (curr < end)
continue;
}
else if (unlikely(!ansi_quotes && *curr == '"' &&
state != tokenize_state::QUOTED_TOKEN_BACKTICK))
{
/*
ANSI_QUOTES is not set, only allow double quotes within backticks.
*/
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "path", text.str);
return true;
}
auto len = cs->charlen(curr, end);
switch (state)
{
case tokenize_state::START:
if (*curr == '`' || (ansi_quotes && *curr == '"'))
{
state= *curr == '`' ? tokenize_state::QUOTED_TOKEN_BACKTICK :
tokenize_state::QUOTED_TOKEN_DOUBLE;
token_start= ++curr;
}
else if (*curr == ',' || my_isspace(cs, (uchar) *curr))
{
case START:
if (*curr == ',' || my_isspace(cs, (uchar) *curr))
curr++;
}
else
{
state= tokenize_state::UNQUOTED_TOKEN;
last_non_space= token_start= curr++;
}
break;
case tokenize_state::QUOTED_TOKEN_BACKTICK:
if (*curr == '`')
{
if (curr + 1 < end && curr[1] == '`')
if (m_count >= array_elements(m_schemas))
{
/* Looked-ahead and found double backtick */
curr += 2;
last_non_space= curr - 1;
my_error(ER_VALUE_TOO_LONG, MYF(0), "PATH");
goto err;
}
m_schemas[m_count].str= to;
if (*curr == '`' || (*curr == '"' && ansi_quotes))
state= (tokenize_state) *curr++;
else if (*curr == '"')
goto err_bad_val;
else
{
state= tokenize_state::END;
token_end= last_non_space + 1;
curr++;
state= UNQUOTED_TOKEN;
while (len--)
*to++= *curr++;
}
}
else
{
if (!my_isspace(cs, (uchar) *curr))
last_non_space= curr;
curr++;
}
break;
case tokenize_state::QUOTED_TOKEN_DOUBLE:
if (*curr == '"')
case QUOTED_TOKEN_BACKTICK:
case QUOTED_TOKEN_DOUBLE:
if (*curr == state)
{
state= tokenize_state::END;
token_end= last_non_space + 1;
curr++;
}
else
{
if (!my_isspace(cs, (uchar) *curr))
last_non_space= curr;
curr++;
if (curr >= end || *curr != state)
{
state= END;
if (add_schema(&to))
goto err_bad_val;
break;
}
}
while (len--)
*to++= *curr++;
break;
case tokenize_state::UNQUOTED_TOKEN:
case UNQUOTED_TOKEN:
if (*curr == ',' || my_isspace(cs, (uchar) *curr))
{
state= *curr++ == ',' ? START : END;
if (add_schema(&to))
goto err_bad_val;
break;
}
else if (*curr == '`' || *curr == '"')
goto err_bad_val;
else
while (len--)
*to++= *curr++;
break;
case END:
if (*curr == ',')
{
state= tokenize_state::END;
token_end= last_non_space + 1;
curr++;
}
else if (unlikely(*curr == '`' || *curr == '"'))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "path", text.str);
return true;
}
else
{
if (!my_isspace(cs, (uchar) *curr))
last_non_space= curr;
curr++;
}
break;
case tokenize_state::END:
state= START;
else if (!my_isspace(cs, (uchar) *curr))
goto err_bad_val;
curr++;
break;
}
if (state == tokenize_state::END)
{
if (token_end > token_start)
{
if (unlikely(add_schema(thd, token_start,
(size_t)(token_end - token_start))))
return true;
}
state= tokenize_state::START;
}
}
if (state == tokenize_state::UNQUOTED_TOKEN)
switch (state)
{
token_end= last_non_space + 1;
auto len= (size_t)(token_end - token_start);
if (len && add_schema(thd, token_start, len))
return true;
case START:
case END:
break;
case QUOTED_TOKEN_BACKTICK:
case QUOTED_TOKEN_DOUBLE:
goto err_bad_val;
case UNQUOTED_TOKEN:
if (add_schema(&to))
goto err_bad_val;
break;
}
else if (unlikely(state == tokenize_state::QUOTED_TOKEN_BACKTICK ||
state == tokenize_state::QUOTED_TOKEN_DOUBLE))
{
/*
Unclosed quoted string.
*/
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "path", text.str);
return true;
}
if (!m_count)
my_free(buf);
return false;
err_bad_val:
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "PATH", text.str);
err:
m_count= 0;
my_free(buf);
return true;
}
@@ -518,7 +429,7 @@ size_t Sql_path::print(char *dst, size_t nbytes_available) const
if (dst < start + nbytes_available)
*dst= '\0';
return dst - start;
}
@@ -546,7 +457,7 @@ LEX_CSTRING Sql_path::lex_cstring(MEM_ROOT *mem_root) const
Sql_path_instant_set::Sql_path_instant_set(THD *thd, const LEX_CSTRING &str)
: m_thd(thd), m_path(std::move(thd->variables.path))
{
if (thd->variables.path.from_text(thd, system_charset_info, str))
if (thd->variables.path.from_text(thd, str))
{
thd->variables.path.set(std::move(m_path));
m_thd= NULL;
@@ -556,7 +467,7 @@ Sql_path_instant_set::Sql_path_instant_set(THD *thd, const LEX_CSTRING &str)
Sql_path_instant_set::Sql_path_instant_set(THD *thd, const Sql_path &new_path)
: m_thd(thd), m_path(std::move(thd->variables.path))
{
thd->variables.path.set(thd, new_path);
thd->variables.path.set(new_path);
}
Sql_path_instant_set::~Sql_path_instant_set()

View File

@@ -49,10 +49,9 @@ public:
/*
Set the variable to the value of rhs, making a copy of the buffer
@param thd The thread handle
@param rhs The path variable to copy from
*/
void set(THD *thd, const Sql_path &rhs);
void set(const Sql_path &rhs);
/*
Set the variable to the value of rhs, moving the buffer
@@ -63,11 +62,12 @@ public:
/*
Parse a string and set the path variable to the parsed value
The string is in my_charset_utf8mb3_general_ci.
@param thd The thread handle
@param cs The character set of the string
@param str The string to parse
*/
bool from_text(THD *thd, CHARSET_INFO *cs, const LEX_CSTRING &str);
bool from_text(THD *thd, const LEX_CSTRING &str);
/*
Get the number of bytes needed to print the path variable
@@ -103,19 +103,7 @@ protected:
bool *resolved) const;
private:
/*
Add a schema to the path variable
@param thd The thread handle
@param schema_str The schema name
@param schema_len The length of the schema name
@return true if the schema could not be added
*/
bool add_schema(THD *thd, const char *schema_str, size_t schema_len);
bool add_schema_direct(const char *schema_str, size_t schema_len);
bool add_schema(char **to);
bool is_cur_schema(const LEX_CSTRING &schema) const;
};

View File

@@ -1946,8 +1946,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
continue;
}
if (sp->m_sql_path.from_text(thd, sql_path))
goto err_with_lex_cleanup;
sp->m_sql_mode= sql_mode;
sp->m_sql_path.from_text(thd, system_charset_info, sql_path);
sp->set_creation_ctx(creation_ctx);
if (!trg_definer || !trg_definer->length)

View File

@@ -3097,7 +3097,7 @@ private:
String *value, buffer;
if (!(value= item->val_str_ascii(&buffer)))
return true;
return path->from_text(thd, value->charset(), value->to_lex_cstring());
return path->from_text(thd, value->to_lex_cstring());
}
static const uchar *make_value_ptr(THD *thd, const Sql_path &path)
@@ -3125,7 +3125,7 @@ private:
void session_save_default(THD *thd, set_var *var) override
{
thd->variables.path.set(thd, global_system_variables.path);
thd->variables.path.set(global_system_variables.path);
}
void global_save_default(THD *thd, set_var *var) override