From f6a576f0ec04a9b2fa2982e2e9188d874bbd156c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 8 Apr 2024 10:07:30 +0200 Subject: Merging upstream version 1.27.2. Signed-off-by: Daniel Baumann --- mycli/AUTHORS | 13 ++- mycli/__init__.py | 2 +- mycli/clitoolbar.py | 5 +- mycli/completion_refresher.py | 9 +- mycli/key_bindings.py | 8 +- mycli/magic.py | 12 ++- mycli/main.py | 27 ++++-- mycli/sqlcompleter.py | 215 ++++++++++++++++++++++++++++++++++++------ mycli/sqlexecute.py | 45 ++++++++- 9 files changed, 278 insertions(+), 58 deletions(-) (limited to 'mycli') diff --git a/mycli/AUTHORS b/mycli/AUTHORS index a805465..e9c73aa 100644 --- a/mycli/AUTHORS +++ b/mycli/AUTHORS @@ -1,11 +1,7 @@ -Project Lead: -------------- - * Thomas Roten - - Core Developers: ---------------- + * Thomas Roten * Irina Truong * Matheus Rosa * Darik Gamble @@ -35,6 +31,7 @@ Contributors: * Daniel Black * Daniel West * Daniël van Eeden + * Fabrizio Gennari * François Pietka * Frederic Aoustin * Georgy Frolov @@ -94,6 +91,12 @@ Contributors: * Arvind Mishra * Kevin Schmeichel * Mel Dafert + * Thomas Copper + * Will Wang + * Alfred Wingate + * Zhanze Wang + * Houston Wong + Created by: ----------- diff --git a/mycli/__init__.py b/mycli/__init__.py index 1512b41..b5476c1 100644 --- a/mycli/__init__.py +++ b/mycli/__init__.py @@ -1 +1 @@ -__version__ = '1.26.1' +__version__ = '1.27.2' diff --git a/mycli/clitoolbar.py b/mycli/clitoolbar.py index 24d1108..52b6ee4 100644 --- a/mycli/clitoolbar.py +++ b/mycli/clitoolbar.py @@ -7,8 +7,7 @@ from .packages import special def create_toolbar_tokens_func(mycli, show_fish_help): """Return a function that generates the toolbar tokens.""" def get_toolbar_tokens(): - result = [] - result.append(('class:bottom-toolbar', ' ')) + result = [('class:bottom-toolbar', ' ')] if mycli.multi_line: delimiter = special.get_current_delimiter() @@ -26,7 +25,7 @@ def create_toolbar_tokens_func(mycli, show_fish_help): '[F3] Multiline: OFF ')) if mycli.prompt_app.editing_mode == EditingMode.VI: result.append(( - 'class:botton-toolbar.on', + 'class:bottom-toolbar.on', 'Vi-mode ({})'.format(_get_vi_mode()) )) diff --git a/mycli/completion_refresher.py b/mycli/completion_refresher.py index 8eb3de9..5d5f40f 100644 --- a/mycli/completion_refresher.py +++ b/mycli/completion_refresher.py @@ -3,7 +3,7 @@ from .packages.special.main import COMMANDS from collections import OrderedDict from .sqlcompleter import SQLCompleter -from .sqlexecute import SQLExecute +from .sqlexecute import SQLExecute, ServerSpecies class CompletionRefresher(object): @@ -113,6 +113,8 @@ def refresh_users(completer, executor): @refresher('functions') def refresh_functions(completer, executor): completer.extend_functions(executor.functions()) + if executor.server_info.species == ServerSpecies.TiDB: + completer.extend_functions(completer.tidb_functions, builtin=True) @refresher('special_commands') def refresh_special(completer, executor): @@ -121,3 +123,8 @@ def refresh_special(completer, executor): @refresher('show_commands') def refresh_show_commands(completer, executor): completer.extend_show_items(executor.show_candidates()) + +@refresher('keywords') +def refresh_keywords(completer, executor): + if executor.server_info.species == ServerSpecies.TiDB: + completer.extend_keywords(completer.tidb_keywords, replace=True) diff --git a/mycli/key_bindings.py b/mycli/key_bindings.py index 03e4ace..443233f 100644 --- a/mycli/key_bindings.py +++ b/mycli/key_bindings.py @@ -1,6 +1,6 @@ import logging from prompt_toolkit.enums import EditingMode -from prompt_toolkit.filters import completion_is_selected, emacs_mode, vi_mode +from prompt_toolkit.filters import completion_is_selected, emacs_mode from prompt_toolkit.key_binding import KeyBindings _logger = logging.getLogger(__name__) @@ -61,7 +61,6 @@ def mycli_bindings(mycli): else: b.start_completion(select_first=False) - @kb.add('>', filter=vi_mode) @kb.add('c-x', 'p', filter=emacs_mode) def _(event): """ @@ -72,7 +71,7 @@ def mycli_bindings(mycli): _logger.debug('Detected /> key.') b = event.app.current_buffer - cursorpos_relative = b.cursor_position / len(b.text) + cursorpos_relative = b.cursor_position / max(1, len(b.text)) pretty_text = mycli.handle_prettify_binding(b.text) if len(pretty_text) > 0: b.text = pretty_text @@ -82,7 +81,6 @@ def mycli_bindings(mycli): cursorpos_abs -= 1 b.cursor_position = min(cursorpos_abs, len(b.text)) - @kb.add('<', filter=vi_mode) @kb.add('c-x', 'u', filter=emacs_mode) def _(event): """ @@ -93,7 +91,7 @@ def mycli_bindings(mycli): _logger.debug('Detected /< key.') b = event.app.current_buffer - cursorpos_relative = b.cursor_position / len(b.text) + cursorpos_relative = b.cursor_position / max(1, len(b.text)) unpretty_text = mycli.handle_unprettify_binding(b.text) if len(unpretty_text) > 0: b.text = unpretty_text diff --git a/mycli/magic.py b/mycli/magic.py index aad229a..e1611bc 100644 --- a/mycli/magic.py +++ b/mycli/magic.py @@ -19,8 +19,16 @@ def load_ipython_extension(ipython): def mycli_line_magic(line): _logger.debug('mycli magic called: %r', line) parsed = sql.parse.parse(line, {}) - conn = sql.connection.Connection(parsed['connection']) - + # "get" was renamed to "set" in ipython-sql: + # https://github.com/catherinedevlin/ipython-sql/commit/f4283c65aaf68f961e84019e8b939e4a3c501d43 + if hasattr(sql.connection.Connection, "get"): + conn = sql.connection.Connection.get(parsed["connection"]) + else: + try: + conn = sql.connection.Connection.set(parsed["connection"]) + # a new positional argument was added to Connection.set in version 0.4.0 of ipython-sql + except TypeError: + conn = sql.connection.Connection.set(parsed["connection"], False) try: # A corresponding mycli object already exists mycli = conn._mycli diff --git a/mycli/main.py b/mycli/main.py index 208572d..ce4dff7 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -93,6 +93,7 @@ SUPPORT_INFO = ( class MyCli(object): default_prompt = '\\t \\u@\\h:\\d> ' + default_prompt_splitln = '\\u@\\h\\n(\\t):\\d>' max_len_prompt = 45 defaults_suffix = None @@ -427,6 +428,7 @@ class MyCli(object): port = 3306 if not host or host == 'localhost': socket = ( + socket or cnf['socket'] or cnf['default_socket'] or guess_socket_location() @@ -589,7 +591,7 @@ class MyCli(object): statements = sqlglot.parse(text, read='mysql') except Exception as e: statements = [] - if len(statements) == 1: + if len(statements) == 1 and statements[0]: pretty_text = statements[0].sql(pretty=True, pad=4, dialect='mysql') else: pretty_text = '' @@ -603,7 +605,7 @@ class MyCli(object): statements = sqlglot.parse(text, read='mysql') except Exception as e: statements = [] - if len(statements) == 1: + if len(statements) == 1 and statements[0]: unpretty_text = statements[0].sql(pretty=False, dialect='mysql') else: unpretty_text = '' @@ -643,7 +645,7 @@ class MyCli(object): def get_message(): prompt = self.get_prompt(self.prompt_format) if self.prompt_format == self.default_prompt and len(prompt) > self.max_len_prompt: - prompt = self.get_prompt('\\d> ') + prompt = self.get_prompt(self.default_prompt_splitln) prompt = prompt.replace("\\x1b", "\x1b") return ANSI(prompt) @@ -1038,7 +1040,7 @@ class MyCli(object): for result in results: title, cur, headers, status = result self.formatter.query = query - output = self.format_output(title, cur, headers) + output = self.format_output(title, cur, headers, special.is_expanded_output()) for line in output: click.echo(line, nl=new_line) @@ -1135,6 +1137,9 @@ class MyCli(object): @click.option('--ssl-key', help='X509 key in PEM format.', type=click.Path(exists=True)) @click.option('--ssl-cipher', help='SSL cipher to use.') +@click.option('--tls-version', + type=click.Choice(['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'], case_sensitive=False), + help='TLS protocol version for secure connection.') @click.option('--ssl-verify-server-cert', is_flag=True, help=('Verify server\'s "Common Name" in its cert against ' 'hostname used when connecting. This option is disabled ' @@ -1186,8 +1191,8 @@ def cli(database, user, host, port, socket, password, dbname, version, verbose, prompt, logfile, defaults_group_suffix, defaults_file, login_path, auto_vertical_output, local_infile, ssl_enable, ssl_ca, ssl_capath, ssl_cert, ssl_key, ssl_cipher, - ssl_verify_server_cert, table, csv, warn, execute, myclirc, dsn, - list_dsn, ssh_user, ssh_host, ssh_port, ssh_password, + tls_version, ssl_verify_server_cert, table, csv, warn, execute, + myclirc, dsn, list_dsn, ssh_user, ssh_host, ssh_port, ssh_password, ssh_key_filename, list_ssh_config, ssh_config_path, ssh_config_host, init_command, charset, password_file): """A MySQL terminal client with auto-completion and syntax highlighting. @@ -1246,6 +1251,7 @@ def cli(database, user, host, port, socket, password, dbname, 'key': ssl_key and os.path.expanduser(ssl_key), 'capath': ssl_capath, 'cipher': ssl_cipher, + 'tls_version': tls_version, 'check_hostname': ssl_verify_server_cert, } @@ -1278,7 +1284,7 @@ def cli(database, user, host, port, socket, password, dbname, uri = urlparse(dsn_uri) if not database: database = uri.path[1:] # ignore the leading fwd slash - if not user: + if not user and uri.username is not None: user = unquote(uri.username) if not password and uri.password is not None: password = unquote(uri.password) @@ -1331,7 +1337,12 @@ def cli(database, user, host, port, socket, password, dbname, try: if csv: mycli.formatter.format_name = 'csv' - elif not table: + if execute.endswith(r'\G'): + execute = execute[:-2] + elif table: + if execute.endswith(r'\G'): + execute = execute[:-2] + else: mycli.formatter.format_name = 'tsv' mycli.run_query(execute) diff --git a/mycli/sqlcompleter.py b/mycli/sqlcompleter.py index 3656aa6..ab42fb8 100644 --- a/mycli/sqlcompleter.py +++ b/mycli/sqlcompleter.py @@ -13,33 +13,178 @@ _logger = logging.getLogger(__name__) class SQLCompleter(Completer): - keywords = ['ACCESS', 'ADD', 'ALL', 'ALTER TABLE', 'AND', 'ANY', 'AS', - 'ASC', 'AUTO_INCREMENT', 'BEFORE', 'BEGIN', 'BETWEEN', - 'BIGINT', 'BINARY', 'BY', 'CASE', 'CHANGE MASTER TO', 'CHAR', - 'CHARACTER SET', 'CHECK', 'COLLATE', 'COLUMN', 'COMMENT', - 'COMMIT', 'CONSTRAINT', 'CREATE', 'CURRENT', - 'CURRENT_TIMESTAMP', 'DATABASE', 'DATE', 'DECIMAL', 'DEFAULT', - 'DELETE FROM', 'DESC', 'DESCRIBE', 'DROP', - 'ELSE', 'END', 'ENGINE', 'ESCAPE', 'EXISTS', 'FILE', 'FLOAT', - 'FOR', 'FOREIGN KEY', 'FORMAT', 'FROM', 'FULL', 'FUNCTION', - 'GRANT', 'GROUP BY', 'HAVING', 'HOST', 'IDENTIFIED', 'IN', - 'INCREMENT', 'INDEX', 'INSERT INTO', 'INT', 'INTEGER', - 'INTERVAL', 'INTO', 'IS', 'JOIN', 'KEY', 'LEFT', 'LEVEL', - 'LIKE', 'LIMIT', 'LOCK', 'LOGS', 'LONG', 'MASTER', - 'MEDIUMINT', 'MODE', 'MODIFY', 'NOT', 'NULL', 'NUMBER', - 'OFFSET', 'ON', 'OPTION', 'OR', 'ORDER BY', 'OUTER', 'OWNER', - 'PASSWORD', 'PORT', 'PRIMARY', 'PRIVILEGES', 'PROCESSLIST', - 'PURGE', 'REFERENCES', 'REGEXP', 'RENAME', 'REPAIR', 'RESET', - 'REVOKE', 'RIGHT', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', - 'SAVEPOINT', 'SELECT', 'SESSION', 'SET', 'SHARE', 'SHOW', - 'SLAVE', 'SMALLINT', 'SMALLINT', 'START', 'STOP', 'TABLE', - 'THEN', 'TINYINT', 'TO', 'TRANSACTION', 'TRIGGER', 'TRUNCATE', - 'UNION', 'UNIQUE', 'UNSIGNED', 'UPDATE', 'USE', 'USER', - 'USING', 'VALUES', 'VARCHAR', 'VIEW', 'WHEN', 'WHERE', 'WITH'] + keywords = [ + 'SELECT', 'FROM', 'WHERE', 'UPDATE', 'DELETE FROM', 'GROUP BY', + 'JOIN', 'INSERT INTO', 'LIKE', 'LIMIT', 'ACCESS', 'ADD', 'ALL', + 'ALTER TABLE', 'AND', 'ANY', 'AS', 'ASC', 'AUTO_INCREMENT', + 'BEFORE', 'BEGIN', 'BETWEEN', 'BIGINT', 'BINARY', 'BY', 'CASE', + 'CHANGE MASTER TO', 'CHAR', 'CHARACTER SET', 'CHECK', 'COLLATE', + 'COLUMN', 'COMMENT', 'COMMIT', 'CONSTRAINT', 'CREATE', 'CURRENT', + 'CURRENT_TIMESTAMP', 'DATABASE', 'DATE', 'DECIMAL', 'DEFAULT', + 'DESC', 'DESCRIBE', 'DROP', 'ELSE', 'END', 'ENGINE', 'ESCAPE', + 'EXISTS', 'FILE', 'FLOAT', 'FOR', 'FOREIGN KEY', 'FORMAT', 'FULL', + 'FUNCTION', 'GRANT', 'HAVING', 'HOST', 'IDENTIFIED', 'IN', + 'INCREMENT', 'INDEX', 'INT', 'INTEGER', 'INTERVAL', 'INTO', 'IS', + 'KEY', 'LEFT', 'LEVEL', 'LOCK', 'LOGS', 'LONG', 'MASTER', + 'MEDIUMINT', 'MODE', 'MODIFY', 'NOT', 'NULL', 'NUMBER', 'OFFSET', + 'ON', 'OPTION', 'OR', 'ORDER BY', 'OUTER', 'OWNER', 'PASSWORD', + 'PORT', 'PRIMARY', 'PRIVILEGES', 'PROCESSLIST', 'PURGE', + 'REFERENCES', 'REGEXP', 'RENAME', 'REPAIR', 'RESET', 'REVOKE', + 'RIGHT', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SAVEPOINT', + 'SESSION', 'SET', 'SHARE', 'SHOW', 'SLAVE', 'SMALLINT', 'SMALLINT', + 'START', 'STOP', 'TABLE', 'THEN', 'TINYINT', 'TO', 'TRANSACTION', + 'TRIGGER', 'TRUNCATE', 'UNION', 'UNIQUE', 'UNSIGNED', 'USE', + 'USER', 'USING', 'VALUES', 'VARCHAR', 'VIEW', 'WHEN', 'WITH' + ] + + tidb_keywords = [ + "SELECT", "FROM", "WHERE", "DELETE FROM", "UPDATE", "GROUP BY", + "JOIN", "INSERT INTO", "LIKE", "LIMIT", "ACCOUNT", "ACTION", "ADD", + "ADDDATE", "ADMIN", "ADVISE", "AFTER", "AGAINST", "AGO", + "ALGORITHM", "ALL", "ALTER", "ALWAYS", "ANALYZE", "AND", "ANY", + "APPROX_COUNT_DISTINCT", "APPROX_PERCENTILE", "AS", "ASC", "ASCII", + "ATTRIBUTES", "AUTO_ID_CACHE", "AUTO_INCREMENT", "AUTO_RANDOM", + "AUTO_RANDOM_BASE", "AVG", "AVG_ROW_LENGTH", "BACKEND", "BACKUP", + "BACKUPS", "BATCH", "BEGIN", "BERNOULLI", "BETWEEN", "BIGINT", + "BINARY", "BINDING", "BINDINGS", "BINDING_CACHE", "BINLOG", "BIT", + "BIT_AND", "BIT_OR", "BIT_XOR", "BLOB", "BLOCK", "BOOL", "BOOLEAN", + "BOTH", "BOUND", "BRIEF", "BTREE", "BUCKETS", "BUILTINS", "BY", + "BYTE", "CACHE", "CALL", "CANCEL", "CAPTURE", "CARDINALITY", + "CASCADE", "CASCADED", "CASE", "CAST", "CAUSAL", "CHAIN", "CHANGE", + "CHAR", "CHARACTER", "CHARSET", "CHECK", "CHECKPOINT", "CHECKSUM", + "CIPHER", "CLEANUP", "CLIENT", "CLIENT_ERRORS_SUMMARY", + "CLUSTERED", "CMSKETCH", "COALESCE", "COLLATE", "COLLATION", + "COLUMN", "COLUMNS", "COLUMN_FORMAT", "COLUMN_STATS_USAGE", + "COMMENT", "COMMIT", "COMMITTED", "COMPACT", "COMPRESSED", + "COMPRESSION", "CONCURRENCY", "CONFIG", "CONNECTION", + "CONSISTENCY", "CONSISTENT", "CONSTRAINT", "CONSTRAINTS", + "CONTEXT", "CONVERT", "COPY", "CORRELATION", "CPU", "CREATE", + "CROSS", "CSV_BACKSLASH_ESCAPE", "CSV_DELIMITER", "CSV_HEADER", + "CSV_NOT_NULL", "CSV_NULL", "CSV_SEPARATOR", + "CSV_TRIM_LAST_SEPARATORS", "CUME_DIST", "CURRENT", "CURRENT_DATE", + "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", + "CURRENT_USER", "CURTIME", "CYCLE", "DATA", "DATABASE", + "DATABASES", "DATE", "DATETIME", "DATE_ADD", "DATE_SUB", "DAY", + "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DDL", + "DEALLOCATE", "DECIMAL", "DEFAULT", "DEFINER", "DELAYED", + "DELAY_KEY_WRITE", "DENSE_RANK", "DEPENDENCY", "DEPTH", "DESC", + "DESCRIBE", "DIRECTORY", "DISABLE", "DISABLED", "DISCARD", "DISK", + "DISTINCT", "DISTINCTROW", "DIV", "DO", "DOT", "DOUBLE", "DRAINER", + "DROP", "DRY", "DUAL", "DUMP", "DUPLICATE", "DYNAMIC", "ELSE", + "ENABLE", "ENABLED", "ENCLOSED", "ENCRYPTION", "END", "ENFORCED", + "ENGINE", "ENGINES", "ENUM", "ERROR", "ERRORS", "ESCAPE", + "ESCAPED", "EVENT", "EVENTS", "EVOLVE", "EXACT", "EXCEPT", + "EXCHANGE", "EXCLUSIVE", "EXECUTE", "EXISTS", "EXPANSION", + "EXPIRE", "EXPLAIN", "EXPR_PUSHDOWN_BLACKLIST", "EXTENDED", + "EXTRACT", "FALSE", "FAST", "FAULTS", "FETCH", "FIELDS", "FILE", + "FIRST", "FIRST_VALUE", "FIXED", "FLASHBACK", "FLOAT", "FLUSH", + "FOLLOWER", "FOLLOWERS", "FOLLOWER_CONSTRAINTS", "FOLLOWING", + "FOR", "FORCE", "FOREIGN", "FORMAT", "FULL", "FULLTEXT", + "FUNCTION", "GENERAL", "GENERATED", "GET_FORMAT", "GLOBAL", + "GRANT", "GRANTS", "GROUPS", "GROUP_CONCAT", "HASH", "HAVING", + "HELP", "HIGH_PRIORITY", "HISTOGRAM", "HISTOGRAMS_IN_FLIGHT", + "HISTORY", "HOSTS", "HOUR", "HOUR_MICROSECOND", "HOUR_MINUTE", + "HOUR_SECOND", "IDENTIFIED", "IF", "IGNORE", "IMPORT", "IMPORTS", + "IN", "INCREMENT", "INCREMENTAL", "INDEX", "INDEXES", "INFILE", + "INNER", "INPLACE", "INSERT_METHOD", "INSTANCE", + "INSTANT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", + "INTEGER", "INTERNAL", "INTERSECT", "INTERVAL", "INTO", + "INVISIBLE", "INVOKER", "IO", "IPC", "IS", "ISOLATION", "ISSUER", + "JOB", "JOBS", "JSON", "JSON_ARRAYAGG", "JSON_OBJECTAGG", "KEY", + "KEYS", "KEY_BLOCK_SIZE", "KILL", "LABELS", "LAG", "LANGUAGE", + "LAST", "LASTVAL", "LAST_BACKUP", "LAST_VALUE", "LEAD", "LEADER", + "LEADER_CONSTRAINTS", "LEADING", "LEARNER", "LEARNERS", + "LEARNER_CONSTRAINTS", "LEFT", "LESS", "LEVEL", "LINEAR", "LINES", + "LIST", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", + "LOCK", "LOCKED", "LOGS", "LONG", "LONGBLOB", "LONGTEXT", + "LOW_PRIORITY", "MASTER", "MATCH", "MAX", "MAXVALUE", + "MAX_CONNECTIONS_PER_HOUR", "MAX_IDXNUM", "MAX_MINUTES", + "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", + "MAX_USER_CONNECTIONS", "MB", "MEDIUMBLOB", "MEDIUMINT", + "MEDIUMTEXT", "MEMORY", "MERGE", "MICROSECOND", "MIN", "MINUTE", + "MINUTE_MICROSECOND", "MINUTE_SECOND", "MINVALUE", "MIN_ROWS", + "MOD", "MODE", "MODIFY", "MONTH", "NAMES", "NATIONAL", "NATURAL", + "NCHAR", "NEVER", "NEXT", "NEXTVAL", "NEXT_ROW_ID", "NO", + "NOCACHE", "NOCYCLE", "NODEGROUP", "NODE_ID", "NODE_STATE", + "NOMAXVALUE", "NOMINVALUE", "NONCLUSTERED", "NONE", "NORMAL", + "NOT", "NOW", "NOWAIT", "NO_WRITE_TO_BINLOG", "NTH_VALUE", "NTILE", + "NULL", "NULLS", "NUMERIC", "NVARCHAR", "OF", "OFF", "OFFSET", + "ON", "ONLINE", "ONLY", "ON_DUPLICATE", "OPEN", "OPTIMISTIC", + "OPTIMIZE", "OPTION", "OPTIONAL", "OPTIONALLY", + "OPT_RULE_BLACKLIST", "OR", "ORDER", "OUTER", "OUTFILE", "OVER", + "PACK_KEYS", "PAGE", "PARSER", "PARTIAL", "PARTITION", + "PARTITIONING", "PARTITIONS", "PASSWORD", "PERCENT", + "PERCENT_RANK", "PER_DB", "PER_TABLE", "PESSIMISTIC", "PLACEMENT", + "PLAN", "PLAN_CACHE", "PLUGINS", "POLICY", "POSITION", "PRECEDING", + "PRECISION", "PREDICATE", "PREPARE", "PRESERVE", + "PRE_SPLIT_REGIONS", "PRIMARY", "PRIMARY_REGION", "PRIVILEGES", + "PROCEDURE", "PROCESS", "PROCESSLIST", "PROFILE", "PROFILES", + "PROXY", "PUMP", "PURGE", "QUARTER", "QUERIES", "QUERY", "QUICK", + "RANGE", "RANK", "RATE_LIMIT", "READ", "REAL", "REBUILD", "RECENT", + "RECOVER", "RECURSIVE", "REDUNDANT", "REFERENCES", "REGEXP", + "REGION", "REGIONS", "RELEASE", "RELOAD", "REMOVE", "RENAME", + "REORGANIZE", "REPAIR", "REPEAT", "REPEATABLE", "REPLACE", + "REPLAYER", "REPLICA", "REPLICAS", "REPLICATION", "REQUIRE", + "REQUIRED", "RESET", "RESPECT", "RESTART", "RESTORE", "RESTORES", + "RESTRICT", "RESUME", "REVERSE", "REVOKE", "RIGHT", "RLIKE", + "ROLE", "ROLLBACK", "ROUTINE", "ROW", "ROWS", "ROW_COUNT", + "ROW_FORMAT", "ROW_NUMBER", "RTREE", "RUN", "RUNNING", "S3", + "SAMPLERATE", "SAMPLES", "SAN", "SAVEPOINT", "SCHEDULE", "SECOND", + "SECONDARY_ENGINE", "SECONDARY_LOAD", "SECONDARY_UNLOAD", + "SECOND_MICROSECOND", "SECURITY", "SEND_CREDENTIALS_TO_TIKV", + "SEPARATOR", "SEQUENCE", "SERIAL", "SERIALIZABLE", "SESSION", + "SESSION_STATES", "SET", "SETVAL", "SHARD_ROW_ID_BITS", "SHARE", + "SHARED", "SHOW", "SHUTDOWN", "SIGNED", "SIMPLE", "SKIP", + "SKIP_SCHEMA_FILES", "SLAVE", "SLOW", "SMALLINT", "SNAPSHOT", + "SOME", "SOURCE", "SPATIAL", "SPLIT", "SQL", "SQL_BIG_RESULT", + "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", + "SQL_NO_CACHE", "SQL_SMALL_RESULT", "SQL_TSI_DAY", "SQL_TSI_HOUR", + "SQL_TSI_MINUTE", "SQL_TSI_MONTH", "SQL_TSI_QUARTER", + "SQL_TSI_SECOND", "SQL_TSI_WEEK", "SQL_TSI_YEAR", "SSL", + "STALENESS", "START", "STARTING", "STATISTICS", "STATS", + "STATS_AUTO_RECALC", "STATS_BUCKETS", "STATS_COL_CHOICE", + "STATS_COL_LIST", "STATS_EXTENDED", "STATS_HEALTHY", + "STATS_HISTOGRAMS", "STATS_META", "STATS_OPTIONS", + "STATS_PERSISTENT", "STATS_SAMPLE_PAGES", "STATS_SAMPLE_RATE", + "STATS_TOPN", "STATUS", "STD", "STDDEV", "STDDEV_POP", + "STDDEV_SAMP", "STOP", "STORAGE", "STORED", "STRAIGHT_JOIN", + "STRICT", "STRICT_FORMAT", "STRONG", "SUBDATE", "SUBJECT", + "SUBPARTITION", "SUBPARTITIONS", "SUBSTRING", "SUM", "SUPER", + "SWAPS", "SWITCHES", "SYSTEM", "SYSTEM_TIME", "TABLE", "TABLES", + "TABLESAMPLE", "TABLESPACE", "TABLE_CHECKSUM", "TARGET", + "TELEMETRY", "TELEMETRY_ID", "TEMPORARY", "TEMPTABLE", + "TERMINATED", "TEXT", "THAN", "THEN", "TIDB", "TIFLASH", + "TIKV_IMPORTER", "TIME", "TIMESTAMP", "TIMESTAMPADD", + "TIMESTAMPDIFF", "TINYBLOB", "TINYINT", "TINYTEXT", "TLS", "TO", + "TOKUDB_DEFAULT", "TOKUDB_FAST", "TOKUDB_LZMA", "TOKUDB_QUICKLZ", + "TOKUDB_SMALL", "TOKUDB_SNAPPY", "TOKUDB_UNCOMPRESSED", + "TOKUDB_ZLIB", "TOP", "TOPN", "TRACE", "TRADITIONAL", "TRAILING", + "TRANSACTION", "TRIGGER", "TRIGGERS", "TRIM", "TRUE", + "TRUE_CARD_COST", "TRUNCATE", "TYPE", "UNBOUNDED", "UNCOMMITTED", + "UNDEFINED", "UNICODE", "UNION", "UNIQUE", "UNKNOWN", "UNLOCK", + "UNSIGNED", "USAGE", "USE", "USER", "USING", "UTC_DATE", + "UTC_TIME", "UTC_TIMESTAMP", "VALIDATION", "VALUE", "VALUES", + "VARBINARY", "VARCHAR", "VARCHARACTER", "VARIABLES", "VARIANCE", + "VARYING", "VAR_POP", "VAR_SAMP", "VERBOSE", "VIEW", "VIRTUAL", + "VISIBLE", "VOTER", "VOTERS", "VOTER_CONSTRAINTS", "WAIT", + "WARNINGS", "WEEK", "WEIGHT_STRING", "WHEN", "WIDTH", "WINDOW", + "WITH", "WITHOUT", "WRITE", "X509", "XOR", "YEAR", "YEAR_MONTH", + "ZEROFILL" + ] functions = ['AVG', 'CONCAT', 'COUNT', 'DISTINCT', 'FIRST', 'FORMAT', 'FROM_UNIXTIME', 'LAST', 'LCASE', 'LEN', 'MAX', 'MID', - 'MIN', 'NOW', 'ROUND', 'SUM', 'TOP', 'UCASE', 'UNIX_TIMESTAMP'] + 'MIN', 'NOW', 'ROUND', 'SUM', 'TOP', 'UCASE', + 'UNIX_TIMESTAMP' + ] + + # https://docs.pingcap.com/tidb/dev/tidb-functions + tidb_functions = [ + 'TIDB_BOUNDED_STALENESS', 'TIDB_DECODE_KEY', 'TIDB_DECODE_PLAN', + 'TIDB_IS_DDL_OWNER', 'TIDB_PARSE_TSO', 'TIDB_VERSION', + 'TIDB_DECODE_SQL_DIGESTS', 'VITESS_HASH', 'TIDB_SHARD' + ] + show_items = [] @@ -94,9 +239,12 @@ class SQLCompleter(Completer): def extend_database_names(self, databases): self.databases.extend(databases) - def extend_keywords(self, additional_keywords): - self.keywords.extend(additional_keywords) - self.all_completions.update(additional_keywords) + def extend_keywords(self, keywords, replace=False): + if replace: + self.keywords = keywords + else: + self.keywords.extend(keywords) + self.all_completions.update(keywords) def extend_show_items(self, show_items): for show_item in show_items: @@ -172,7 +320,12 @@ class SQLCompleter(Completer): metadata[self.dbname][relname].append(column) self.all_completions.add(column) - def extend_functions(self, func_data): + def extend_functions(self, func_data, builtin=False): + # if 'builtin' is set this is extending the list of builtin functions + if builtin: + self.functions.extend(func_data) + return + # 'func_data' is a generator object. It can throw an exception while # being consumed. This could happen if the user has launched the app # without specifying a database name. This exception must be handled to @@ -224,13 +377,13 @@ class SQLCompleter(Completer): if fuzzy: regex = '.*?'.join(map(escape, text)) pat = compile('(%s)' % regex) - for item in sorted(collection): + for item in collection: r = pat.search(item.lower()) if r: completions.append((len(r.group()), r.start(), item)) else: match_end_limit = len(text) if start_only else None - for item in sorted(collection): + for item in collection: match_point = item.lower().find(text, 0, match_end_limit) if match_point >= 0: completions.append((len(text), match_point, item)) @@ -244,7 +397,7 @@ class SQLCompleter(Completer): return kw.lower() return (Completion(z if casing is None else apply_case(z), -len(text)) - for x, y, z in sorted(completions)) + for x, y, z in completions) def get_completions(self, document, complete_event, smart_completion=None): word_before_cursor = document.get_word_before_cursor(WORD=True) diff --git a/mycli/sqlexecute.py b/mycli/sqlexecute.py index c019707..bd5f5d9 100644 --- a/mycli/sqlexecute.py +++ b/mycli/sqlexecute.py @@ -56,7 +56,7 @@ class ServerInfo: re_species = ( (r'(?P[0-9\.]+)-MariaDB', ServerSpecies.MariaDB), - (r'(?P[0-9\.]+)[a-z0-9]*-TiDB', ServerSpecies.TiDB), + (r'[0-9\.]*-TiDB-v(?P[0-9\.]+)-?(?P[a-z0-9\-]*)', ServerSpecies.TiDB), (r'(?P[0-9\.]+)[a-z0-9]*-(?P[0-9]+$)', ServerSpecies.Percona), (r'(?P[0-9\.]+)[a-z0-9]*-(?P[A-Za-z0-9_]+)', @@ -176,11 +176,15 @@ class SQLExecute(object): if init_command and len(list(special.split_queries(init_command))) > 1: client_flag |= pymysql.constants.CLIENT.MULTI_STATEMENTS + ssl_context = None + if ssl: + ssl_context = self._create_ssl_ctx(ssl) + conn = pymysql.connect( database=db, user=user, password=password, host=host, port=port, unix_socket=socket, use_unicode=True, charset=charset, autocommit=True, client_flag=client_flag, - local_infile=local_infile, conv=conv, ssl=ssl, program_name="mycli", + local_infile=local_infile, conv=conv, ssl=ssl_context, program_name="mycli", defer_connect=defer_connect, init_command=init_command ) @@ -354,3 +358,40 @@ class SQLExecute(object): def change_db(self, db): self.conn.select_db(db) self.dbname = db + + def _create_ssl_ctx(self, sslp): + import ssl + + ca = sslp.get("ca") + capath = sslp.get("capath") + hasnoca = ca is None and capath is None + ctx = ssl.create_default_context(cafile=ca, capath=capath) + ctx.check_hostname = not hasnoca and sslp.get("check_hostname", True) + ctx.verify_mode = ssl.CERT_NONE if hasnoca else ssl.CERT_REQUIRED + if "cert" in sslp: + ctx.load_cert_chain(sslp["cert"], keyfile=sslp.get("key")) + if "cipher" in sslp: + ctx.set_ciphers(sslp["cipher"]) + + # raise this default to v1.1 or v1.2? + ctx.minimum_version = ssl.TLSVersion.TLSv1 + + if "tls_version" in sslp: + tls_version = sslp["tls_version"] + + if tls_version == "TLSv1": + ctx.minimum_version = ssl.TLSVersion.TLSv1 + ctx.maximum_version = ssl.TLSVersion.TLSv1 + elif tls_version == "TLSv1.1": + ctx.minimum_version = ssl.TLSVersion.TLSv1_1 + ctx.maximum_version = ssl.TLSVersion.TLSv1_1 + elif tls_version == "TLSv1.2": + ctx.minimum_version = ssl.TLSVersion.TLSv1_2 + ctx.maximum_version = ssl.TLSVersion.TLSv1_2 + elif tls_version == "TLSv1.3": + ctx.minimum_version = ssl.TLSVersion.TLSv1_3 + ctx.maximum_version = ssl.TLSVersion.TLSv1_3 + else: + _logger.error('Invalid tls version: %s', tls_version) + + return ctx -- cgit v1.2.3