in mysqlshdk/shellcore/provider_sql.cc [217:483]
Completion_list complete(mysqlshdk::Sql_completion_result &&result) const {
Completion_list list;
const auto add_from_set = [&list](Names *s) {
while (!s->empty()) {
list.emplace_back(std::move(s->extract(s->begin()).value()));
}
};
const auto quote = [&prefix = std::as_const(result.context.prefix)](
const std::string &s, bool as_string) {
if (prefix.quoted_as_identifier) {
return shcore::quote_identifier(s, prefix.quote);
} else if (as_string && prefix.quoted_as_string) {
return shcore::quote_string(s, prefix.quote);
} else {
// we quote here with '`' characters, they are still valid even if
// ANSI_QUOTES is enabled
return shcore::quote_identifier_if_needed(s);
}
};
const auto quote_identifier = ["e](const std::string &s) {
return quote(s, false);
};
const auto quote_string_or_identifier = ["e](const std::string &s) {
return quote(s, true);
};
const auto add_identifiers_and_quote_if_needed =
[&list, "e_identifier](const auto &container) {
for (const auto &item : container) {
list.emplace_back(quote_identifier(item));
}
};
const auto add_identifiers =
[&list, "e_identifier,
&prefix = std::as_const(result.context.prefix.as_identifier)](
const auto &container, bool as_function_call = false) {
for (const auto &item : find_prefix_ci(container, prefix)) {
auto name = quote_identifier(item->name());
if (as_function_call) {
name += '(';
name += ')';
}
list.emplace_back(std::move(name));
}
};
const auto add_strings_or_identifiers =
[&list, "e_string_or_identifier,
&prefix =
std::as_const(result.context.prefix.as_string_or_identifier)](
const auto &container) {
for (const auto &item : find_prefix_ci(container, prefix)) {
list.emplace_back(quote_string_or_identifier(item->name()));
}
};
const auto add_identifiers_from = [&add_identifiers, this](
const Names &schemas, auto source,
bool as_function_call = false) {
for (const auto &schema : schemas) {
if (const auto &s = find(m_instance.schemas, schema)) {
add_identifiers(s->*source, as_function_call);
}
}
};
const auto add_columns = [&add_identifiers, this](const Columns &columns) {
for (const auto &schema : columns) {
if (const auto &s = find(m_instance.schemas, schema.first)) {
for (const auto &table : schema.second) {
if (const auto &t = find(s->tables, table)) {
add_identifiers(t->columns);
}
if (const auto &v = find(s->views, table)) {
add_identifiers(v->columns);
}
}
}
}
};
// these are already filtered
add_from_set(&result.keywords);
add_from_set(&result.system_functions);
add_identifiers_and_quote_if_needed(result.tables);
for (const auto &candidate : result.candidates) {
using Candidate = mysqlshdk::Sql_completion_result::Candidate;
switch (candidate) {
case Candidate::SCHEMA:
add_identifiers(m_instance.schemas);
break;
case Candidate::TABLE:
add_identifiers_from(result.tables_from, &Instance::Schema::tables);
break;
case Candidate::VIEW:
add_identifiers_from(result.views_from, &Instance::Schema::views);
break;
case Candidate::COLUMN:
add_columns(result.columns);
break;
case Candidate::INTERNAL_COLUMN:
add_columns(result.internal_columns);
break;
case Candidate::PROCEDURE:
add_identifiers_from(result.procedures_from,
&Instance::Schema::procedures, true);
break;
case Candidate::FUNCTION:
add_identifiers_from(result.functions_from,
&Instance::Schema::functions, true);
break;
case Candidate::TRIGGER:
add_identifiers_from(result.triggers_from,
&Instance::Schema::triggers);
break;
case Candidate::EVENT:
add_identifiers_from(result.events_from, &Instance::Schema::events);
break;
case Candidate::ENGINE:
add_identifiers(m_instance.engines);
break;
case Candidate::UDF:
add_identifiers(m_instance.udfs, true);
break;
case Candidate::RUNTIME_FUNCTION:
// if prefix was quoted then we cannot match any functions
if (!result.context.prefix.quoted) {
for (const auto &item :
find_prefix_ci(*m_instance.system_functions,
result.context.prefix.full_wide)) {
list.emplace_back(item->name());
}
}
break;
case Candidate::LOGFILE_GROUP:
add_identifiers(m_instance.logfile_groups);
break;
case Candidate::USER_VAR:
// user variables can be quoted as strings or identifiers
add_strings_or_identifiers(m_instance.user_variables);
break;
case Candidate::SYSTEM_VAR:
// system variables can be quoted as identifiers, but if ANSI_QUOTES
// is active, only "sys_var" (and not i.e. @@"sys_var") form is
// recognized; since we don't know the context, we simply disallow
// double quotes
if (!result.context.prefix.quoted ||
'`' == result.context.prefix.quote) {
for (const auto &item :
find_prefix_ci(m_instance.system_variables,
result.context.prefix.as_identifier)) {
auto name = item->name();
if (result.context.prefix.quote) {
name = shcore::quote_identifier(name);
}
list.emplace_back(std::move(name));
}
}
break;
case Candidate::TABLESPACE:
add_identifiers(m_instance.tablespaces);
break;
case Candidate::USER: {
// construct the prefix which matches the format of user accounts
// stored in cache: 'user'@'host'
std::string prefix;
const auto &as_account = result.context.as_account;
using Account_part = parsers::Sql_completion_result::Account_part;
if (!as_account.user.full.empty()) {
const auto add_part = [&prefix](const Account_part &part) {
prefix += shcore::quote_string(part.unquoted, '\'');
};
add_part(as_account.user);
if (as_account.has_at_sign) {
prefix += '@';
if (!as_account.host.full.empty()) {
add_part(as_account.host);
}
}
// remove the last quote to allow for partial matches
if (prefix.back() == '\'') {
prefix.pop_back();
}
}
// recreate candidate based on quotes used by the user
const auto quote_user = [&as_account](const std::string &name) {
const auto quote_like_part = [](const std::string &s,
const Account_part &part) {
if (part.full.empty()) {
return shcore::quote_string(s, '\'');
} else if (!part.quoted) {
return s;
} else {
if (part.quote == '`') {
return shcore::quote_identifier(s);
} else {
return shcore::quote_string(s, part.quote);
}
}
};
const auto account = shcore::split_account(name);
std::string quoted = quote_like_part(account.user, as_account.user);
quoted += '@';
// if host part was not provided, quote it like the user, to keep
// the same style
quoted += quote_like_part(account.host, as_account.host.full.empty()
? as_account.user
: as_account.host);
return quoted;
};
for (const auto &item : find_prefix_ci(m_instance.users, prefix)) {
list.emplace_back(quote_user(item->name()));
}
} break;
case Candidate::CHARSET:
// character sets can be quoted as strings or identifiers
add_strings_or_identifiers(m_instance.charsets);
break;
case Candidate::COLLATION:
// collations can be quoted as strings or identifiers
add_strings_or_identifiers(m_instance.collations);
break;
case Candidate::PLUGIN:
add_identifiers(m_instance.plugins);
break;
case Candidate::LABEL:
// already filtered
add_identifiers_and_quote_if_needed(result.context.labels);
break;
}
}
return list;
}