mysqlshdk/shellcore/shell_options.cc (1,209 lines of code) (raw):

/* * Copyright (c) 2014, 2024, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * * 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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysqlshdk/include/shellcore/shell_options.h" #include <stdlib.h> #include <cstdio> #include <iostream> #include <limits> #include <my_alloc.h> #include <my_default.h> #include <mysql.h> #include "modules/mod_utils.h" #include "mysqlshdk/libs/db/uri_common.h" #include "mysqlshdk/libs/db/uri_parser.h" #include "mysqlshdk/libs/utils/debug.h" #include "mysqlshdk/libs/utils/log_sql.h" #include "mysqlshdk/shellcore/credential_manager.h" #include "shellcore/ishell_core.h" #include "shellcore/shell_notifications.h" #include "shellcore/shell_resultset_dumper.h" #include "utils/utils_file.h" #include "utils/utils_general.h" #include "utils/utils_path.h" #include "utils/utils_string.h" namespace { // We do not want to expose password. Override argv's uri with uri string // without password. void obfuscate_uri_password(const std::string &nopassword_uri, char *argv) { char *value = argv; auto value_length = strlen(value); assert(nopassword_uri.size() <= value_length); // If password is longer than @host:port part then strncpy will leak // password ending. We need to clear whole buffer first. shcore::clear_buffer(value, value_length); strncpy(value, nopassword_uri.c_str(), value_length + 1); value[value_length] = '\0'; } } // namespace namespace mysqlsh { using mysqlshdk::db::Transport_type; using mysqlshdk::db::uri::Type; bool Shell_options::Storage::has_connection_data( bool require_main_options) const { if (require_main_options) { return connection_data.has_user() || connection_data.has_host() || connection_data.has_port() || connection_data.has_socket() || connection_data.has_pipe() || connection_data.has_password() || prompt_password; } return connection_data.has_data() || prompt_password; } mysqlshdk::db::Connection_options Shell_options::Storage::connection_options() const { auto target_server = connection_data; if (no_password && !target_server.has_password()) { target_server.set_password(""); } if (!ssh.uri.empty()) { auto ssh_dict = shcore::make_dict(); if (!ssh.identity_file.empty()) { (*ssh_dict)[mysqlshdk::db::kSshIdentityFile] = shcore::Value(ssh.identity_file); } if (!ssh.config_file.empty()) { (*ssh_dict)[mysqlshdk::db::kSshConfigFile] = shcore::Value(ssh.config_file); } (*ssh_dict)[mysqlshdk::db::kSsh] = shcore::Value(ssh.uri); if (!ssh.pwd.empty()) { (*ssh_dict)[mysqlshdk::db::kSshPassword] = shcore::Value(ssh.pwd); } auto ssh_options = mysqlsh::get_ssh_options(ssh_dict); target_server.set_ssh_options(ssh_options); } if (has_multi_passwords()) { target_server.set_scheme("mysql"); } if (!fido_register_factor.empty()) { target_server.set_unchecked(mysqlshdk::db::kFidoRegisterFactor, fido_register_factor.c_str()); } return target_server; } namespace { mysqlsh::SessionType session_type_for_option(const std::string &option) { if (shcore::str_beginswith(option, "mysql://")) { return mysqlsh::SessionType::Classic; } else if (shcore::str_beginswith(option, "mysqlx://")) { return mysqlsh::SessionType::X; } else if (option == "-mc" || option == "--mc" || option == "--mysql" || option == "--sqlc") { return mysqlsh::SessionType::Classic; } else if (option == "-mx" || option == "--mx" || option == "--mysqlx" || option == "--sqlx") { return mysqlsh::SessionType::X; } else { // -ma, --ma return mysqlsh::SessionType::Auto; } } } // namespace mysqlsh::SessionType Shell_options::Storage::check_option_session_type_conflict( const std::string &option) { auto session_type = session_type_for_option(option); if (session_type == mysqlsh::SessionType::Auto) return session_type; auto format = [](const std::string &opt, bool capitalize) { if (opt[0] == '-') return (capitalize ? "Option " : "option ") + opt; // URIs std::string scheme, rest; std::tie(scheme, rest) = shcore::str_partition_after(opt, "://"); return "URI scheme " + scheme; }; for (const auto &opt : m_session_type_options) { auto opt_type = session_type_for_option(opt); // any combination is allowed so that options can be placed in my.cnf and // overriden from the cmdline, except for --sqlc / --sqlx if (session_type != opt_type && (shcore::str_beginswith(opt, "--sql") || shcore::str_beginswith(option, "--sql"))) { throw shcore::Exception::argument_error(format(option, true) + " cannot be combined with " + format(opt, false)); } } switch (session_type) { case mysqlsh::SessionType::Classic: m_session_type_options.push_back(option); break; case mysqlsh::SessionType::X: m_session_type_options.push_back(option); break; case mysqlsh::SessionType::Auto: break; } return session_type; } void Shell_options::Storage::set_uri(const std::string &uri) { if (uri.empty()) { connection_data = Connection_options(); } else { check_option_session_type_conflict(uri); // URLs always override port/socket/schema connection_data.clear_schema(); connection_data.clear_port(); connection_data.clear_socket(); // don't set defaults to the parsed URI because we don't want to override // options like user, pass and scheme that were specified before the URI // with defaults connection_data.override_with(shcore::get_connection_options(uri, false)); } } using mysqlshdk::db::Ssl_options; using shcore::opts::assign_value; using shcore::opts::cmdline; using shcore::opts::deprecated; using shcore::opts::Source; using std::placeholders::_1; using std::placeholders::_2; void Shell_options::handle_mycnf_options(int *argc, char ***argv, MEM_ROOT *argv_alloc) { constexpr const char *defaults_args_separator = "----args-separator----"; const char *load_default_groups[] = {"mysqlsh", "client", nullptr}; my_getopt_use_args_separator = 1; if (my_load_defaults("my", load_default_groups, argc, argv, argv_alloc, nullptr)) { throw std::runtime_error("Could not read my.cnf files"); } // find the separator between default options and explicit options int sep = 1; while ((*argv)[sep] && strcmp((*argv)[sep], defaults_args_separator) != 0) { // convert my.cnf style options to ours (e.g. --ssl_ca to --ssl-ca) for (int i = 0; (*argv)[sep][i] != '\0' && (*argv)[sep][i] != '='; ++i) { if ((*argv)[sep][i] == '_') (*argv)[sep][i] = '-'; } ++sep; } (*argv)[sep] = nullptr; // process the default options try { handle_cmdline_options( sep, *argv, false, std::bind(&Shell_options::custom_cmdline_handler, this, _1)); } catch (const std::exception &e) { throw std::runtime_error( std::string("While processing defaults options:\n") + e.what()); } (*argv)[sep] = (*argv)[0]; *argv = *argv + sep; *argc = *argc - sep; } namespace { void default_print_err(const std::string &s) { std::cerr << s << std::endl; } void default_print_out(const std::string &s) { std::cout << s << std::endl; } } // namespace Shell_options::Shell_options( int argc, char **argv, const std::string &configuration_file, Option_flags_set flags, const std::function<void(const std::string &)> &on_error, const std::function<void(const std::string &)> &on_warning) : Options(configuration_file), m_on_error(on_error), m_on_warning(on_warning) { if (!m_on_error) { m_on_error = default_print_err; } if (!m_on_warning) { m_on_warning = default_print_out; } std::string home = shcore::get_home_dir(); #ifdef WIN32 home += ("MySQL\\mysql-sandboxes"); #else home += ("mysql-sandboxes"); #endif std::string default_mysql_plugin_dir; #ifdef DEFAULT_MYSQL_PLUGIN_DIR default_mysql_plugin_dir = shcore::path::normalize(shcore::path::join_path( shcore::get_mysqlx_home_path(), DEFAULT_MYSQL_PLUGIN_DIR)); #endif const auto create_result_format_handler = [this](const char *value) { return [this, value](const std::string &opt_name, const char *) { get_option(SHCORE_RESULT_FORMAT) .handle_command_line_input(opt_name, value); }; }; // clang-format off add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (&print_cmd_line_helper, false, cmdline("-?", "--help"), "Display this help and exit.") (cmdline("--"), "Triggers API Command Line integration, which allows execution of " "methods of the Shell global objects from command line using syntax:\n" " -- <object> <method> [arguments]\n" "For more details execute '\\? cmdline' inside of the Shell.") (&storage.execute_statement, "", cmdline("-e", "--execute=<cmd>"), "Execute command and quit.") (cmdline("-c", "--pyc=<cmd>"), "Execute Python command and quit. " "Any options specified after this are used as arguments of the " "processed command.") (cmdline("-f", "--file=<file>"), "Specify a file to process in batch mode. " "Any options specified after this are used as arguments of the " "processed file."); add_startup_options(true) (cmdline("--ssh=<value>"), "Make SSH tunnel connection using" "Uniform Resource Identifier. " "Format: [user@]host[:port]") (&storage.ssh.identity_file, "", cmdline("--ssh-identity-file=<file>"), "File from which the private key for public key authentication is " "read.", [](const std::string &value, Source) { if (shcore::str_caseeq(value, "null")) return std::string(); auto path = shcore::path::expand_user(value); if (!shcore::is_file(path)) { throw std::invalid_argument("ssh-identity-file requires existing " "file."); } shcore::check_file_readable_or_throw(path); shcore::check_file_access_rights_to_open(path); return value; }); add_named_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (&storage.ssh.config_file, "", "ssh.configFile", cmdline("--ssh-config-file=<file>"), "Specify a custom path for SSH " "configuration.", [](const std::string &value, Source) { auto path = shcore::path::expand_user(value); if (shcore::is_file(path)) { shcore::check_file_readable_or_throw(path); shcore::check_file_access_rights_to_open(path); } return value; }) (&storage.ssh.buffer_size, 10240, "ssh.bufferSize", "Set buffer size in bytes for data transfer, default is 10240 (10Kb)", shcore::opts::Range<int>(0, std::numeric_limits<int>::max())); add_startup_options(true) (cmdline("--uri=<value>"), "Connect to Uniform Resource Identifier. " "Format: [user[:pass]@]host[:port][/db]") (cmdline("-h", "--host=<name>"), "Connect to host.", [this](const std::string&, const char* value) { storage.connection_data.set_host(value); }) (cmdline("-P", "--port=<#>"), "Port number to use for connection.", [this](const std::string& option, const char* value) { storage.connection_data.clear_socket(); storage.connection_data.clear_pipe(); try { storage.connection_data.set_port(shcore::opts::convert<int>( value, shcore::opts::Source::Command_line)); } catch (const std::invalid_argument& error) { throw std::invalid_argument(shcore::str_format("Invalid value for %s: %s", option.c_str(), value)); } }) (cmdline("--connect-timeout=<ms>"), "Connection timeout in milliseconds.", [this](const std::string&, const char* value) { storage.connection_data.set(mysqlshdk::db::kConnectTimeout, value); }) #ifndef _WIN32 (cmdline("-S", "--socket[=<sock>]"), "Socket name to use. " "If no value is provided will use default UNIX socket path.", #else (cmdline("-S", "--socket=<sock>"), "Pipe name to use (only classic sessions).", #endif [this](const std::string&, const char* value) { storage.connection_data.clear_port(); storage.connection_data.clear_pipe(); if (value) { #ifdef _WIN32 storage.connection_data.set_pipe(value); #else storage.connection_data.set_socket(value); #endif } else { #ifdef _WIN32 storage.connection_data.set_transport_type(mysqlshdk::db::Pipe); #else storage.connection_data.set_transport_type(mysqlshdk::db::Socket); #endif } } ) (cmdline("-u", "--user=<name>"), "User for the connection to the server.") (cmdline("--dbuser=<name>"), deprecated(m_on_warning, "--user")) (cmdline("--password=[<pass>]"), "Password to use when connecting to server. " "If password is empty, connection will be made without using a password.") (cmdline("--dbpassword[=<pass>]"), deprecated(m_on_warning, "--password")) (cmdline("-p", "--password"), "Request password prompt to set the password") (cmdline("--password1[=<pass>]"), "Password for first factor authentication plugin.") (cmdline("--password2[=<pass>]"), "Password for second factor authentication plugin.", [this](const std::string&, const char* value) { storage.connection_data.set_mfa_password( 1, value == nullptr ? "" : value); }) (cmdline("--password3[=<pass>]"), "Password for third factor authentication plugin.", [this](const std::string&, const char* value) { storage.connection_data.set_mfa_password( 2, value == nullptr ? "" : value); }) (cmdline("-C", "--compress[=<value>]"), "Use compression in client/server protocol. Valid values: 'REQUIRED', " "'PREFFERED', 'DISABLED', 'True', 'False', '1', and '0'. Boolean values" " map to REQUIRED and DISABLED respectively. By default compression is " "set to DISABLED in classic protocol and PREFERRED in X protocol " "connections.", [this](const std::string&, const char* value) { storage.connection_data.set_compression( value == nullptr ? "REQUIRED" : value); }) (cmdline("--compression-algorithms=<list>"), "Use compression algorithm in server/client protocol. Expects comma " "separated list of algorithms. Supported algorithms include " "'zstd', 'zlib', 'lz4' (X protocol only), and 'uncompressed', " "which if appears in a list, causes connection to " "succeed even if compression negotiation fails.", [this](const std::string&, const char* value) { storage.connection_data.set_compression_algorithms(value); }) (cmdline("--compression-level=<int>", "--zstd-compression-level=<int>"), "Use this compression level in the client/server protocol. " "Supported by X protocol and zstd compression in classic protocol.", [this](const std::string& opt, const char* value) { try { storage.connection_data.set_compression_level( shcore::lexical_cast<int64_t>(value)); if (opt == "--zstd-compression-level" && !storage.connection_data.has_compression_algorithms()) storage.connection_data.set_compression_algorithms("zstd"); } catch(...) { throw std::invalid_argument( "The value of 'compression-level' must be an integer."); } }); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (cmdline("--import <file> <collection>", "--import <file> <table> <col>"), "Import JSON documents from file to collection or table in MySQL" " Server. Set file to - if you want to read the data from stdin." " Requires a default schema on the connection options.") (cmdline("-D", "--schema=<name>", "--database=<name>"), "Schema to use.", [this](const std::string&, const char* value) { storage.connection_data.set_schema(value); }) (&storage.fido_register_factor, "", cmdline("--fido-register-factor=<name>"), "Specifies authentication factor, for which registration needs to be " "done.") (cmdline("--recreate-schema"), "Drop and recreate the specified schema. " "Schema will be deleted if it exists!", deprecated(m_on_warning, nullptr, [this](const std::string&, const char* ) { storage.recreate_database = true; })); add_startup_options(true) (cmdline("--mx", "--mysqlx"), "Uses connection data to create an X protocol session.", std::bind( &Shell_options::override_session_type, this, _1, _2)) (cmdline("--mc", "--mysql"), "Uses connection data to create a classic session.", std::bind( &Shell_options::override_session_type, this, _1, _2)); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (cmdline("--redirect-primary"), "Ensure that the target server is part of " "an InnoDB cluster or ReplicaSet and if it is not a primary, find the " "primary and connect to it.", assign_value(&storage.redirect_session, Shell_options::Storage::Redirect_to::Primary)) (cmdline("--redirect-secondary"), "Ensure that the target server is part " "of an InnoDB Cluster or ReplicaSet and if it is not a secondary, find " "a secondary and connect to it.", assign_value(&storage.redirect_session, Shell_options::Storage::Redirect_to::Secondary)) (cmdline("--cluster"), "Ensure that the target server is part of an InnoDB " "Cluster and if so, set the 'cluster' global variable. Also sets " "'clusterset' if the cluster is part of a ClusterSet.", [this](const std::string&, const char* value) { storage.default_cluster = value ? value : ""; storage.default_cluster_set = true; }) (cmdline("--replicaset"), "Ensure that the target server is part of an " "InnoDB ReplicaSet and if so, set the 'rs' global variable.", assign_value(&storage.default_replicaset_set, true)) (cmdline("--sql"), "Start in SQL mode, auto-detecting the protocol to use " "if it is not specified as part of the connection information.", assign_value( &storage.initial_mode, shcore::IShell_core::Mode::SQL)) (cmdline("--sqlc"), "Start in SQL mode using a classic session.", std::bind( &Shell_options::override_session_type, this, _1, _2)) (cmdline("--sqlx"), "Start in SQL mode using an X protocol session.", std::bind( &Shell_options::override_session_type, this, _1, _2)) (cmdline("--js", "--javascript"), "Start in JavaScript mode.", #ifdef HAVE_JS [this](const std::string&, const char*) { storage.initial_mode = shcore::IShell_core::Mode::JavaScript; } #else [](const std::string&, const char*) { throw std::invalid_argument("JavaScript is not supported."); } #endif ) (cmdline("--py", "--python"), "Start in Python mode.", #ifdef HAVE_PYTHON [this](const std::string&, const char*) { storage.initial_mode = shcore::IShell_core::Mode::Python; } #else [](const std::string&, const char*) { throw std::invalid_argument("Python is not supported."); } #endif ) (cmdline("--pym <module>"), "Run Python library module as a script. Remaining args are forwarded to it.") (&storage.wrap_json, "off", cmdline("--json[=<format>]"), "Produce output in JSON format. Allowed values: raw, pretty, and off. " "If no format is specified pretty format is produced.", [](const std::string &val, Source) { if (val == "off") return "off"; if (val.empty() || val == "pretty") return "json"; if (val == "raw") return "json/raw"; throw std::invalid_argument( "Value for --json must be either pretty, raw or off."); }) (cmdline("--table"), "Produce output in table format (default for interactive mode). This " "option can be used to force that format when running in batch mode.", create_result_format_handler("table")) (cmdline("--tabbed"), "Produce output in tab separated format (default for batch mode). This " "option can be used to force that format when running in interactive " "mode.", create_result_format_handler("tabbed")) (cmdline("-E", "--vertical"), "Print the output of a query (rows) vertically.", create_result_format_handler("vertical")); add_named_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (&storage.result_format, "table", "outputFormat", "outputFormat option was deprecated, " "please use " SHCORE_RESULT_FORMAT " to set result format and --json " "command line option to wrap output in JSON instead.", [this](const std::string &val, Source s) { m_on_warning("WARNING: outputFormat option was deprecated, " "please use " SHCORE_RESULT_FORMAT " to set result format and" " --json command line option to wrap output in JSON instead."); get_option(SHCORE_RESULT_FORMAT).set(val, s); storage.wrap_json = shcore::str_beginswith(val, "json") ? val : "off"; return storage.result_format; }) (&storage.result_format, "table", SHCORE_RESULT_FORMAT, cmdline("--result-format=<value>"), "Determines format of results. Allowed values:" " [" RESULTSET_DUMPER_FORMATS "].", [](const std::string &val, Source) { if (!Resultset_dumper_base::is_valid_format(val)) throw std::invalid_argument( "The acceptable values for the option " SHCORE_RESULT_FORMAT " are: " RESULTSET_DUMPER_FORMATS); return val; }) (&storage.interactive, false, SHCORE_INTERACTIVE, "Enables interactive mode", shcore::opts::Read_only<bool>()) (&storage.db_name_cache, true, SHCORE_DB_NAME_CACHE, "Enable database name caching for autocompletion.") (&storage.devapi_schema_object_handles, true, SHCORE_DEVAPI_DB_OBJECT_HANDLES, "Enable table and collection name handles for the DevAPI db object.") (&storage.log_sql_ignore, "*SELECT*:SHOW*", SHCORE_LOG_SQL_IGNORE, "Colon separated list of SQL statement patterns to filter out, unless logSql is set to 'all' or 'unfiltered'." "Default: *SELECT*:SHOW*") (&storage.log_sql_ignore_unsafe, "*IDENTIFIED*:*PASSWORD*", SHCORE_LOG_SQL_IGNORE_UNSAFE, "Colon separated list of SQL statement patterns to filter out, unless logSql is set to 'unfiltered'." "Default: *IDENTIFIED*:*PASSWORD*"); add_startup_options(true) (cmdline("--get-server-public-key"), "Request public key from the server " "required for RSA key pair-based password exchange. Use when connecting" " to MySQL 8.0 servers with classic sessions with SSL mode DISABLED.", [this](const std::string&, const char*) { storage.connection_data.set(mysqlshdk::db::kGetServerPublicKey, "true"); }) (cmdline("--server-public-key-path=<p>"), "The path name to a file " "containing a client-side copy of the public key required by the " "server for RSA key pair-based password exchange. Use when connecting " "to MySQL 8.0 servers with classic sessions with SSL mode DISABLED.", [this](const std::string&, const char* value) { storage.connection_data.set(mysqlshdk::db::kServerPublicKeyPath, value); }); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (cmdline("-i", "--interactive[=full]"), "To use in batch mode. " "It forces emulation of interactive mode processing. Each " "line on the batch is processed as if it were in interactive mode.", [this](const std::string&, const char* value) { if (!value || !value[0]) { storage.interactive = true; storage.full_interactive = false; } else if (strcmp(value, "full") == 0) { storage.interactive = true; storage.full_interactive = true; } else { throw std::invalid_argument( "Value for --interactive if any, must be full"); } }); // make sure hack for accessing log_level via Value works static_assert( std::is_integral< std::underlying_type<shcore::Logger::LOG_LEVEL>::type>::value, "Invalid underlying type of shcore::Logger::LOG_LEVEL enum"); add_named_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (&storage.force, false, SHCORE_BATCH_CONTINUE_ON_ERROR, cmdline("--force"), "In SQL batch mode, forces processing to continue if an error " "is found.", shcore::opts::Read_only<bool>()) (&storage.log_file, shcore::path::join_path(shcore::get_user_config_path(), "mysqlsh.log"), SHCORE_LOG_FILE_NAME, cmdline("--log-file=<path>"), "Override location of the Shell log file.", shcore::opts::Read_only<std::string>()) // read-only in shell.options (reinterpret_cast<int*>(&storage.log_level), shcore::Logger::LOG_INFO, "logLevel", cmdline("--log-level=<value>"), std::string("Set logging level. ") + shcore::Logger::get_level_range_info(), [this](const std::string &val, Source) { const char* value = val.c_str(); if (*value == '@') { storage.log_to_stderr = true; value++; } else { storage.log_to_stderr = false; } const auto level = shcore::Logger::parse_log_level(value); if (const auto logger = shcore::current_logger(true)) { if (storage.log_to_stderr) { logger->log_to_stderr(); } else { logger->stop_log_to_stderr(); } logger->set_log_level(level); } return level; }) (&storage.dba_log_sql, 0, SHCORE_DBA_LOG_SQL, cmdline("--dba-log-sql[={0|1|2}]"), "Log SQL statements executed by AdminAPI operations: " "0 - logging disabled; 1 - log statements other than SELECT and SHOW; " "2 - log all statements. Option takes precedence over --log-sql in " "Dba.* context if enabled.", shcore::opts::Range<int>(0, 2)) (&storage.log_sql, "error", SHCORE_LOG_SQL, cmdline("--log-sql=off|error|on|all|unfiltered"), "Log SQL statements: " "off - none of SQL statements will be logged; " "(default) error - SQL statement with error message will be logged " "only when error occurs; " "on - All SQL statements will be logged except these which match " "any of logSql.ignorePattern and logSql.ignorePatternUnsafe glob pattern; " "all - All SQL statements will be logged except these which match " "any of logSql.ignorePatternUnsafe glob pattern; " "unfiltered - All SQL statements will be logged.", [](const std::string &val, Source) { shcore::Log_sql::parse_log_level(val); // Accept |val| if |parse_log_level()| not throw return val; } ) (&storage.history_sql_syslog, false, SHCORE_HISTORY_SQL_SYSLOG, cmdline("--syslog"), "Log filtered interactive commands to the system log. Filtering of " "commands depends on the patterns supplied via histignore option.") (&storage.verbose_level, 0, SHCORE_VERBOSE, cmdline("--verbose[={0|1|2|3|4}]"), "Enable diagnostic message output to the console: 0 - display no " "messages; 1 - display error, warning and informational messages; 2, 3," " 4 - include higher levels of debug messages. If level is not given, 1" " is assumed.", shcore::opts::Range<int>(0, 4)) (&storage.passwords_from_stdin, false, "passwordsFromStdin", cmdline("--passwords-from-stdin"), "Read passwords from stdin instead of the console.") (&storage.show_warnings, true, SHCORE_SHOW_WARNINGS, cmdline("--show-warnings={true|false}"), "Automatically display SQL warnings on SQL mode if available.") (&storage.show_column_type_info, false, "showColumnTypeInfo", cmdline("--column-type-info"), "Display column type information in SQL mode. Please be aware that " "output may depend on the protocol you are using to connect to the " "server, e.g. DbType field is approximated when using X protocol.") (&storage.history_max_size, 1000, SHCORE_HISTORY_MAX_SIZE, "Shell's history maximum size", shcore::opts::Range<int>(0, std::numeric_limits<int>::max())) (&storage.histignore, "*IDENTIFIED*:*PASSWORD*", SHCORE_HISTIGNORE, cmdline("--histignore=<filters>"), "Shell's history ignore list.") (&storage.history_autosave, false, SHCORE_HISTORY_AUTOSAVE, "Shell's history autosave.") (&storage.sandbox_directory, home, SHCORE_SANDBOX_DIR, "Default sandbox directory") (&storage.dba_gtid_wait_timeout, 60, SHCORE_DBA_GTID_WAIT_TIMEOUT, "Timeout value in seconds to wait for GTIDs to be synchronized.", shcore::opts::Range<int>(0, std::numeric_limits<int>::max())) (&storage.dba_restart_wait_timeout, 60, SHCORE_DBA_RESTART_WAIT_TIMEOUT, "Timeout in seconds to wait for MySQL server to come back after a " "restart during clone recovery.", shcore::opts::Range<int>(0, std::numeric_limits<int>::max())) (&storage.dba_connectivity_checks, true, SHCORE_DBA_CONNECTIVITY_CHECKS, "Checks SSL settings and network connectivity between instances when " "creating a cluster, replicaset or clusterset, or adding an instance " "to one.") (&storage.wizards, true, SHCORE_USE_WIZARDS, "Enables wizard mode.") (&storage.initial_mode, shcore::IShell_core::Mode::None, "defaultMode", "Specifies the shell mode to use when shell is started " "- one of sql, js or py.", std::bind(&shcore::parse_mode, _1), [](shcore::IShell_core::Mode mode) { return shcore::to_string(mode); }) (&storage.pager, "", SHCORE_PAGER, "PAGER", cmdline("--pager=<value>"), "Pager used to display text output of statements executed in SQL mode " "as well as some other selected commands. Pager can be manually " "enabled in scripting modes. If you don't supply an " "option, the default pager is taken from your ENV variable PAGER. " "This option only works in interactive mode. This option is disabled " "by default.") (&storage.default_compress, false, SHCORE_DEFAULT_COMPRESS, "Enable compression in client/server protocol by default " "in global shell sessions.") (&storage.oci_config_file, shcore::path::join_path(shcore::path::home(), ".oci", "config"), "oci.configFile", "Path to Oracle Cloud Infrastructure (OCI) configuration file.") (&storage.oci_profile, std::string{"DEFAULT"}, "oci.profile", "Oracle Cloud Infrastructure (OCI) configuration file profile name.") (&storage.mysql_plugin_dir, default_mysql_plugin_dir, SHCORE_MYSQL_PLUGIN_DIR, cmdline("--mysql-plugin-dir[=<path>]"), "Directory for client-side authentication plugins.") (&storage.connect_timeout, 10.0, SHCORE_CONNECT_TIMEOUT, "Default connection timeout used by Shell sessions.", shcore::opts::Non_negative<double>()) (&storage.dba_connect_timeout, 5.0, SHCORE_DBA_CONNECT_TIMEOUT, "Default connection timeout used for sessions created in AdminAPI " "operations.", shcore::opts::Non_negative<double>()); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (cmdline("--name-cache"), "Enable database name caching for autocompletion and DevAPI (default).", [this](const std::string &, const char *) { storage.db_name_cache = true; storage.db_name_cache_set = true; storage.devapi_schema_object_handles = true; }) (cmdline("-A", "--no-name-cache"), "Disable automatic database name caching for autocompletion and DevAPI. " "Use \\rehash to load DB names manually.", [this](const std::string &, const char *) { storage.db_name_cache = false; storage.db_name_cache_set = true; storage.devapi_schema_object_handles = false; }) (cmdline("--nw", "--no-wizard"), "Disables wizard mode.", assign_value(&storage.wizards, false)) (&storage.no_password, false, cmdline("--no-password"), "Sets empty password and disables prompt for password.") (cmdline("-V", "--version"), "Prints the version of MySQL Shell.", [this](const std::string&, const char*) { if (print_cmd_line_version) { print_cmd_line_version_extra = true; } print_cmd_line_version = true; }); add_startup_options(true) (cmdline("--ssl-key=<file_name>"), "The path to the SSL private key file in PEM format.", std::bind(&Ssl_options::set_key, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-cert=<file_name>"), "The path to the SSL public key certificate file in PEM format.", std::bind(&Ssl_options::set_cert, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-ca=<file_name>"), "The path to the certificate authority file in PEM format.", std::bind(&Ssl_options::set_ca, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-capath=<dir_name>"), "The path to the directory that contains the certificate authority " "files in PEM format.", std::bind(&Ssl_options::set_capath, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-cipher=<cipher_list>"), "The list of permissible encryption ciphers for connections that use " "TLS protocols up through TLSv1.2.", std::bind(&Ssl_options::set_cipher, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-crl=<file_name>"), "The path to the file containing certificate revocation lists in PEM " "format.", std::bind(&Ssl_options::set_crl, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-crlpath=<dir_name>"), "The path to the directory that contains certificate revocation-list " "files in PEM format.", std::bind(&Ssl_options::set_crlpath, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--ssl-mode=<mode>"), "SSL mode to use. Allowed values: DISABLED," "PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY.", std::bind(&Shell_options::set_ssl_mode, this, _1, _2)) (cmdline("--tls-version=<version>"), "TLS version to use. Allowed values: TLSv1.2, TLSv1.3.", std::bind(&Ssl_options::set_tls_version, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--tls-ciphersuites=<name>"), "TLS v1.3 cipher to use.", std::bind(&Ssl_options::set_tls_ciphersuites, &storage.connection_data.get_ssl_options(), _2)) (cmdline("--auth-method=<method>"), "Authentication method to use. In case of classic session, this is the " "name of the authentication plugin to use, i.e. caching_sha2_password. " "In case of X protocol session, it should be one of: AUTO, " "FROM_CAPABILITIES, FALLBACK, MYSQL41, PLAIN, SHA256_MEMORY.", [this](const std::string&, const char* value) { storage.connection_data.set(mysqlshdk::db::kAuthMethod, value); }); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) (&storage.execute_dba_statement, "", cmdline("--dba=enableXProtocol"), "Enable the X protocol in the target " "server. Requires a connection using classic session.") (cmdline("--trace-proto"), assign_value(&storage.trace_protocol, true)); add_startup_options() (cmdline("--ssl[=<opt>]"), deprecated(m_on_warning, "--ssl-mode", std::bind(&Shell_options::set_ssl_mode, this, _1, _2), "REQUIRED", { {"1", "REQUIRED"}, {"YES", "REQUIRED"}, {"0", "DISABLED"}, {"NO", "DISABLED"} })) (cmdline("--node"), deprecated(m_on_warning, "--mysqlx", std::bind( &Shell_options::override_session_type, this, _1, _2))) (cmdline("--classic"), deprecated(m_on_warning, "--mysql", std::bind( &Shell_options::override_session_type, this, _1, _2))) (cmdline("--sqln"), deprecated(m_on_warning, "--sqlx", std::bind( &Shell_options::override_session_type, this, _1, _2))); add_startup_options(!flags.is_set(Option_flags::CONNECTION_ONLY)) ( cmdline("--quiet-start[={1|2}]"), "Avoids printing information when the shell is started. A value of " "1 will prevent printing the shell version information. A value of " "2 will prevent printing any information unless it is an error. " "If no value is specified uses 1 as default.", [this](const std::string&, const char* value) { if (!value || !value[0] || !strcmp(value, "1")) { storage.quiet_start = Shell_options::Quiet_start::SUPRESS_BANNER; storage.full_interactive = false; } else if (strcmp(value, "2") == 0) { storage.quiet_start = Shell_options::Quiet_start::SUPRESS_INFO; } else { throw std::invalid_argument("Value for --quiet-start if any, must be any of 1 or 2"); } }) (cmdline("--debug=<control>"), [this](const std::string &, const char* value) { #ifdef NDEBUG // If DBUG is disabled, we just print a warning saying the option won't // do anything. This is to keep options compatible between build types m_on_warning("WARNING: This build of mysqlsh has the DBUG feature " "disabled. --debug option ignored."); #endif storage.dbug_options = value ? value : ""; }) ; // <-- Note this is on purpose: Mark the termination of the option definition. // clang-format on if (!flags.is_set(Option_flags::CONNECTION_ONLY)) shcore::Credential_manager::get().register_options(this); try { MEM_ROOT argv_alloc{}; mysqlshdk::db::Connection_options mycnf_connection_data; handle_environment_options(); if (!configuration_file.empty()) { handle_persisted_options(); } // process standard my.cnf files and cmdline options first, which will // prepend argv with options read from these places and end with a // ----args-separator---- if (flags.is_set(Option_flags::READ_MYCNF)) handle_mycnf_options(&argc, &argv, &argv_alloc); mycnf_connection_data = storage.connection_data; // temporarily clear passwords so we can detect if we got them from cmdline storage.connection_data.clear_password(); storage.connection_data.clear_mfa_passwords(); handle_cmdline_options( argc, argv, false, std::bind(&Shell_options::custom_cmdline_handler, this, _1)); if (storage.connection_data.has_password() || storage.connection_data.has_mfa_passwords()) { got_cmdline_password = true; } else { // if we didn't get any password in the cmdline, restore the ones we got // from my.cnf (if any) if (mycnf_connection_data.has_password()) storage.connection_data.set_password( mycnf_connection_data.get_password()); if (mycnf_connection_data.has_mfa_passwords()) storage.connection_data.set_mfa_passwords( mycnf_connection_data.get_mfa_passwords()); } // default to mysql:// if we got connection options from my.cnf but we still // don't know what session type to use if (storage.has_connection_data() && !storage.connection_data.has_scheme() && mycnf_connection_data.has_data()) storage.connection_data.set_scheme("mysql"); check_password_conflicts(); check_ssh_conflicts(); if (!flags.is_set(Option_flags::CONNECTION_ONLY)) { check_file_execute_conflicts(); check_import_options(); check_result_format(); } check_connection_options(); if (!flags.is_set(Option_flags::CONNECTION_ONLY)) shcore::Logger::set_stderr_output_format(storage.wrap_json); } catch (const std::exception &e) { m_on_error(e.what()); storage.exit_code = 1; } } static inline std::string value_to_string(const shcore::Value &value) { return value.type == shcore::Value_type::String ? value.get_string() : value.repr(); } void Shell_options::set(const std::string &option, const shcore::Value &value) { try { shcore::Options::set(option, value_to_string(value)); } catch (const std::exception &e) { throw shcore::Exception::argument_error(e.what()); } } void Shell_options::notify(const std::string &option) { shcore::Value::Map_type_ref info = shcore::Value::new_map().as_map(); (*info)["option"] = shcore::Value(option); (*info)["value"] = get(option); shcore::ShellNotifications::get()->notify(SN_SHELL_OPTION_CHANGED, nullptr, info); } void Shell_options::set_and_notify(const std::string &option, const std::string &value, bool save_to_file) { try { Options::set(option, value); notify(option); if (save_to_file) save(option); } catch (const std::exception &e) { throw shcore::Exception::argument_error(e.what()); } } void Shell_options::set_and_notify(const std::string &option, const shcore::Value &value, bool save_to_file) { set_and_notify(option, value_to_string(value), save_to_file); } void Shell_options::unset(const std::string &option, bool save_to_file) { if (save_to_file) unsave(option); get_option(option).reset_to_default_value(); notify(option); } shcore::Value Shell_options::get(const std::string &option) { using shcore::opts::Concrete_option; auto it = named_options.find(option); if (it == named_options.end()) throw std::invalid_argument("No option registered under name: " + option); Concrete_option<bool> *opt_bool = dynamic_cast<Concrete_option<bool> *>(it->second); if (opt_bool != nullptr) return shcore::Value(opt_bool->get()); Concrete_option<int> *opt_int = dynamic_cast<Concrete_option<int> *>(it->second); if (opt_int != nullptr) return shcore::Value(opt_int->get()); { const auto opt_double = dynamic_cast<Concrete_option<double> *>(it->second); if (opt_double != nullptr) return shcore::Value(opt_double->get()); } auto opt_set_string = dynamic_cast<Concrete_option<std::vector<std::string>> *>(it->second); if (opt_set_string != nullptr) { auto value = shcore::Value::new_array(); auto array = value.as_array(); for (const auto &val : opt_set_string->get()) { array->emplace_back(val); } return value; } return shcore::Value(get_value_as_string(option)); } std::vector<std::string> Shell_options::get_named_options() { std::vector<std::string> res; for (const auto &op : named_options) res.push_back(op.first); return res; } static void handle_missing_value(shcore::Options::Iterator *it) { if (nullptr == it->value() || '\0' == it->value()[0]) { throw std::invalid_argument(std::string(it->iterator()->first()) + ": option " + it->option() + " requires an argument"); } } bool Shell_options::custom_cmdline_handler(Iterator *iterator) { const auto option = iterator->option(); if ("--" == option) { if (m_shell_cli_operation) throw std::invalid_argument( "MySQL Shell can handle only one operation at a time"); iterator->next_no_value(); m_shell_cli_operation.reset(new shcore::cli::Shell_cli_operation()); m_shell_cli_operation->parse(iterator->iterator()); } else if ("--file" == option || "-f" == option) { handle_missing_value(iterator); const std::string file = iterator->value(); iterator->next(); storage.run_file = file; if (shcore::str_endswith(file, ".js")) { storage.initial_mode = shcore::IShell_core::Mode::JavaScript; } else if (shcore::str_endswith(file, ".py")) { storage.initial_mode = shcore::IShell_core::Mode::Python; } else if (shcore::str_endswith(file, ".sql")) { storage.initial_mode = shcore::IShell_core::Mode::SQL; } // the rest of the cmdline options, starting from here are all passed // through to the script storage.script_argv.push_back(file); const auto cmdline = iterator->iterator(); while (cmdline->valid()) storage.script_argv.push_back(cmdline->get()); } else if ("-c" == option || "--pyc" == option) { // Like --py -f , but with an inline command // Needed for backwards compatibility with python executable when // subprocess spawns it #ifdef HAVE_PYTHON handle_missing_value(iterator); storage.initial_mode = shcore::IShell_core::Mode::Python; storage.execute_statement = iterator->value(); iterator->next(); // the rest of the cmdline options, starting from the next one are all // passed through to the script storage.script_argv.push_back("-c"); const auto cmdline = iterator->iterator(); while (cmdline->valid()) storage.script_argv.push_back(cmdline->get()); #else throw std::invalid_argument("Python is not supported."); #endif } else if ("--pym" == option) { #ifndef HAVE_PYTHON throw std::invalid_argument("Python is not supported."); #endif handle_missing_value(iterator); const std::string module = iterator->value(); iterator->next(); storage.run_module = module; storage.initial_mode = shcore::IShell_core::Mode::Python; // the rest of the cmdline options, starting from here are all passed // through to the script storage.script_argv.push_back(module); const auto cmdline = iterator->iterator(); while (cmdline->valid()) storage.script_argv.push_back(cmdline->get()); } else if ("--uri" == option || '-' != option[0]) { handle_missing_value(iterator); storage.set_uri(iterator->value()); { char *value = const_cast<char *>(iterator->value()); const auto nopwd = mysqlshdk::db::uri::hide_password_in_uri(value); obfuscate_uri_password(nopwd, value); } iterator->next(); } else if ("--user" == option || "--dbuser" == option || "-u" == option) { handle_missing_value(iterator); if ("--dbuser" == option) { // Deprecated handler is not going to be invoked, need to inform the // user that --dbuser should not longer be used. // Console is not available at this time, need to use stdout. m_on_warning( "WARNING: The --dbuser option was deprecated, " "please use --user instead."); } storage.connection_data.set_user(iterator->value()); iterator->next(); } else if ("--ssh" == option) { handle_missing_value(iterator); storage.ssh.uri = iterator->value(); storage.ssh.uri_data = shcore::get_ssh_connection_options(storage.ssh.uri, false); if (storage.ssh.uri_data.has_password()) { char *value = const_cast<char *>(iterator->value()); const auto nopwd = mysqlshdk::db::uri::hide_password_in_uri(value); obfuscate_uri_password(nopwd, value); } iterator->next(); } else if ("--password" == option || "-p" == option || "--dbpassword" == option || "--password1" == option) { // Note that in any connection attempt, password prompt will be done if // the password is missing. // The behavior of the password cmd line argument is as follows: // ARGUMENT EFFECT // --password forces password prompt no matter it was already // provided // --password value forces password prompt no matter it was already // provided (value is not taken as password) // --password= sets password to empty (password is available but // empty so it will not be prompted) // -p<value> sets the password to <value> // --password=<value> sets the password to <value> if ("--dbpassword" == option) { // Deprecated handler is not going to be invoked, need to inform the // user that --dbpassword should not longer be used. // Console is not available at this time, need to use stdout. m_on_warning( "WARNING: The --dbpassword option was deprecated, " "please use --password instead."); } if (shcore::Options::Iterator::Type::NO_VALUE == iterator->type()) { storage.prompt_password = true; iterator->next_no_value(); } else if (shcore::Options::Iterator::Type::SEPARATE_VALUE != iterator->type()) { // --password=value || -pvalue const auto value = iterator->value(); storage.connection_data.set_mfa_password(0, value); const std::string stars(strlen(value), '*'); strncpy(const_cast<char *>(value), stars.c_str(), stars.length() + 1); iterator->next(); storage.prompt_password = false; } else { // --password value (value is ignored) storage.prompt_password = true; iterator->next_no_value(); } } else if ("--import" == option) { m_on_warning( "WARNING: The --import option was deprecated and will be removed in " "a future version of the MySQL Shell. Please consider using the CLI " "call for import-json instead.\nFor additional information: mysqlsh -- " "util import-json --help"); const auto cmdline = iterator->iterator(); storage.import_args.push_back(cmdline->get()); // omit --import // Gets the positional arguments for --import // As they define the target database object for the data while (cmdline->valid()) { // We append --import arguments until next shell option in program // argument list, i.e. -* or --*. Single char '-' is a --import argument. const char *arg = cmdline->peek(); if (arg[0] == '-' && arg[1] != '\0') { break; } storage.import_args.push_back(cmdline->get()); } if (storage.import_args[1] == "-") { // STDIN import will be handled directly in main // Here we store the import options while (cmdline->valid()) storage.import_opts.push_back(cmdline->get()); } else { // Non STDIN import is handled as a CLI API call if (m_shell_cli_operation) throw std::invalid_argument( "MySQL Shell can handle only one operation at a time"); m_shell_cli_operation = std::make_unique<shcore::cli::Shell_cli_operation>(); m_shell_cli_operation->set_object_name("util"); m_shell_cli_operation->set_method_name("importJson"); m_shell_cli_operation->add_cmdline_argument(storage.import_args.at(1)); // Parses the positional arguments to set them on the CLI operation switch (storage.import_args.size()) { case 4: { const std::string &target = storage.import_args.at(2); const std::string &column = storage.import_args.at(3); m_shell_cli_operation->add_cmdline_argument("--table=" + target); m_shell_cli_operation->add_cmdline_argument("--tableColumn=" + column); break; } case 3: { const std::string &target = storage.import_args.at(2); m_shell_cli_operation->add_cmdline_argument("--collection=" + target); break; } case 2: break; default: throw std::runtime_error( "Usage: --import <filename> [<collection>|<table> <column>] " "[options]"); } // All of the options above are valid m_shell_cli_operation->parse(cmdline); } } else if ("-m" == option) { bool handled = false; if (iterator->value()) { const std::string value = option + iterator->value(); const char *replacement = nullptr; if ("-ma" == value) { handled = true; } else if ("-mc" == value) { replacement = "--mc"; } else if ("-mx" == value) { replacement = "--mx"; } if (nullptr != replacement) { handled = true; } if (handled) { deprecated(m_on_warning, replacement, std::bind(&Shell_options::override_session_type, this, _1, _2))(value, nullptr); iterator->next(); } } if (!handled) { throw std::invalid_argument(iterator->iterator()->first() + std::string(": unknown option -m")); } } else if ("--dba-log-sql" == option) { m_on_warning( "WARNING: The --dba-log-sql option was deprecated, " "please use --log-sql instead."); return false; } else { return false; } return true; } void Shell_options::override_session_type(const std::string &option, const char *value) { if (value != nullptr) throw std::invalid_argument("Option " + option + " does not support value"); auto session_type = storage.check_option_session_type_conflict(option); if (option.find("--sql") != std::string::npos) storage.initial_mode = shcore::IShell_core::Mode::SQL; switch (session_type) { case mysqlsh::SessionType::Classic: storage.connection_data.set_scheme("mysql"); break; case mysqlsh::SessionType::X: storage.connection_data.set_scheme("mysqlx"); break; case mysqlsh::SessionType::Auto: break; } } void Shell_options::set_ssl_mode(const std::string &option, const char *value) { int mode = mysqlshdk::db::MapSslModeNameToValue::get_value(value); if (mode == 0) { throw std::invalid_argument(option + " must be any of [DISABLED, PREFERRED, " "REQUIRED, VERIFY_CA, VERIFY_IDENTITY]"); } storage.connection_data.get_ssl_options().set_mode( static_cast<mysqlshdk::db::Ssl_mode>(mode)); } void Shell_options::check_password_conflicts() { if (!storage.ssh.pwd.empty() && storage.ssh.uri_data.has_password()) { if (storage.ssh.pwd != storage.ssh.uri_data.get_password()) { const auto error = "Conflicting options: provided SSH password differs from the " "password in the SSH-URI."; throw std::runtime_error(error); } } if (storage.no_password && storage.prompt_password) { const auto error = "Conflicting options: --password and --no-password option cannot be " "used together."; throw std::runtime_error(error); } if (storage.no_password && storage.connection_data.has_password() && !storage.connection_data.get_password().empty()) { const auto error = "Conflicting options: --no-password cannot be used if password is " "provided."; throw std::runtime_error(error); } } void Shell_options::check_file_execute_conflicts() { if (!storage.run_file.empty() && !storage.execute_statement.empty()) { throw std::runtime_error( "Conflicting options: --execute and --file cannot be used at the same " "time."); } else if (!storage.run_module.empty() && !storage.execute_statement.empty()) { throw std::runtime_error( "Conflicting options: --execute and --pym cannot be used at the same " "time."); } // note: --file and --pym are impossible because both consume remaining args } void Shell_options::check_ssh_conflicts() { if (!storage.ssh.uri.empty() && !storage.connection_data.has_data()) { throw std::runtime_error( "SSH tunnel can't be started because DB URI is missing"); } if (!storage.ssh.uri.empty() && storage.connection_data.has_socket()) { throw std::runtime_error( "Socket connections through SSH are not supported"); } } void Shell_options::check_result_format() { if (storage.wrap_json != "off" && get_option_source(SHCORE_RESULT_FORMAT) == Source::Command_line && storage.wrap_json != storage.result_format) throw std::invalid_argument(shcore::str_format( "Conflicting options: " SHCORE_RESULT_FORMAT " cannot be set to '%s' when " "--json option implying '%s' value is used.", storage.result_format.c_str(), storage.wrap_json.c_str())); } void Shell_options::check_import_options() { bool is_schema_set = storage.connection_data.has_schema(); if (!storage.import_args.empty()) { if (!storage.has_connection_data()) { const auto error = "Error: --import requires an active session."; throw std::runtime_error(error); } if (!is_schema_set) { const auto error = "Error: --import requires a default schema on the active session."; throw std::runtime_error(error); } } } void Shell_options::check_connection_options() { if (!storage.fido_register_factor.empty()) { if (!storage.connection_data.has_data()) { m_on_warning( "WARNING: --fido-register-factor was specified without " "connection data, the option will be ignored."); } else if (storage.connection_data.has_scheme() && storage.connection_data.get_scheme() == "mysqlx") { throw std::runtime_error( "Option --fido-register-factor is only available for MySQL protocol " "connections."); } } if (storage.has_multi_passwords()) { if (storage.connection_data.has_scheme() && storage.connection_data.get_scheme() == "mysqlx") throw shcore::Exception::argument_error( "Multi-factor authentication is only compatible with MySQL protocol"); } } } // namespace mysqlsh