mycli 2.1.1__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. {mycli-2.1.1 → mycli-2.3.0}/.github/workflows/ci.yml +4 -4
  2. {mycli-2.1.1 → mycli-2.3.0}/.github/workflows/publish.yml +4 -4
  3. {mycli-2.1.1 → mycli-2.3.0}/.github/workflows/typecheck.yml +1 -1
  4. {mycli-2.1.1 → mycli-2.3.0}/AGENTS.md +9 -5
  5. {mycli-2.1.1/mycli.egg-info → mycli-2.3.0}/PKG-INFO +30 -13
  6. {mycli-2.1.1 → mycli-2.3.0}/README.md +29 -12
  7. {mycli-2.1.1 → mycli-2.3.0}/changelog.md +52 -0
  8. {mycli-2.1.1 → mycli-2.3.0}/mycli/TIPS +9 -1
  9. {mycli-2.1.1 → mycli-2.3.0}/mycli/cli_runner.py +97 -11
  10. {mycli-2.1.1 → mycli-2.3.0}/mycli/clibuffer.py +1 -0
  11. {mycli-2.1.1 → mycli-2.3.0}/mycli/client.py +10 -1
  12. {mycli-2.1.1 → mycli-2.3.0}/mycli/client_connection.py +56 -6
  13. {mycli-2.1.1 → mycli-2.3.0}/mycli/client_query.py +2 -0
  14. {mycli-2.1.1 → mycli-2.3.0}/mycli/completion_refresher.py +56 -21
  15. {mycli-2.1.1 → mycli-2.3.0}/mycli/main.py +24 -0
  16. {mycli-2.1.1 → mycli-2.3.0}/mycli/main_modes/execute.py +3 -3
  17. {mycli-2.1.1 → mycli-2.3.0}/mycli/main_modes/repl.py +17 -0
  18. {mycli-2.1.1 → mycli-2.3.0}/mycli/myclirc +56 -8
  19. {mycli-2.1.1 → mycli-2.3.0}/mycli/output.py +34 -5
  20. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/__init__.py +4 -0
  21. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/dsn_aliases.py +4 -0
  22. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/iocommands.py +11 -1
  23. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/main.py +9 -1
  24. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/utils.py +53 -14
  25. {mycli-2.1.1 → mycli-2.3.0}/mycli/schema_prefetcher.py +27 -3
  26. {mycli-2.1.1 → mycli-2.3.0}/mycli/sqlexecute.py +10 -0
  27. {mycli-2.1.1 → mycli-2.3.0}/mycli/ssh_tunnel.py +74 -17
  28. mycli-2.3.0/mycli/vault.py +54 -0
  29. {mycli-2.1.1 → mycli-2.3.0/mycli.egg-info}/PKG-INFO +30 -13
  30. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/SOURCES.txt +3 -1
  31. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/scm_file_list.json +130 -128
  32. mycli-2.3.0/mycli.egg-info/scm_version.json +8 -0
  33. {mycli-2.1.1 → mycli-2.3.0}/test/features/fixture_data/help_commands.txt +8 -7
  34. {mycli-2.1.1 → mycli-2.3.0}/test/myclirc +55 -7
  35. mycli-2.3.0/test/pytests/test_cli_runner.py +1033 -0
  36. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_client.py +10 -0
  37. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_client_connection.py +185 -14
  38. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_completion_refresher.py +133 -1
  39. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_main.py +28 -0
  40. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_main_modes_repl.py +47 -1
  41. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_output.py +102 -0
  42. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_schema_prefetcher.py +140 -0
  43. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_iocommands.py +2 -2
  44. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_utils.py +51 -0
  45. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_sqlexecute.py +15 -5
  46. mycli-2.3.0/test/pytests/test_ssh_tunnel.py +512 -0
  47. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_tabular_output.py +5 -0
  48. mycli-2.3.0/test/pytests/test_vault.py +96 -0
  49. {mycli-2.1.1 → mycli-2.3.0}/test/utils.py +1 -0
  50. mycli-2.1.1/mycli.egg-info/scm_version.json +0 -8
  51. mycli-2.1.1/test/pytests/test_cli_runner.py +0 -476
  52. mycli-2.1.1/test/pytests/test_ssh_tunnel.py +0 -307
  53. {mycli-2.1.1 → mycli-2.3.0}/.git-blame-ignore-revs +0 -0
  54. {mycli-2.1.1 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  55. {mycli-2.1.1 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  56. {mycli-2.1.1 → mycli-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  57. {mycli-2.1.1 → mycli-2.3.0}/.github/dependabot.yml +0 -0
  58. {mycli-2.1.1 → mycli-2.3.0}/.github/workflows/lint.yml +0 -0
  59. {mycli-2.1.1 → mycli-2.3.0}/.gitignore +0 -0
  60. {mycli-2.1.1 → mycli-2.3.0}/AUTHORS.rst +0 -0
  61. {mycli-2.1.1 → mycli-2.3.0}/CONTRIBUTING.md +0 -0
  62. {mycli-2.1.1 → mycli-2.3.0}/LICENSE.txt +0 -0
  63. {mycli-2.1.1 → mycli-2.3.0}/MANIFEST.in +0 -0
  64. {mycli-2.1.1 → mycli-2.3.0}/SPONSORS.rst +0 -0
  65. {mycli-2.1.1 → mycli-2.3.0}/doc/key_bindings.rst +0 -0
  66. {mycli-2.1.1 → mycli-2.3.0}/doc/llm.md +0 -0
  67. {mycli-2.1.1 → mycli-2.3.0}/doc/screenshots/main.gif +0 -0
  68. {mycli-2.1.1 → mycli-2.3.0}/doc/screenshots/tables.png +0 -0
  69. {mycli-2.1.1 → mycli-2.3.0}/mycli/AUTHORS +0 -0
  70. {mycli-2.1.1 → mycli-2.3.0}/mycli/SPONSORS +0 -0
  71. {mycli-2.1.1 → mycli-2.3.0}/mycli/__init__.py +0 -0
  72. {mycli-2.1.1 → mycli-2.3.0}/mycli/app_state.py +0 -0
  73. {mycli-2.1.1 → mycli-2.3.0}/mycli/client_commands.py +0 -0
  74. {mycli-2.1.1 → mycli-2.3.0}/mycli/clistyle.py +0 -0
  75. {mycli-2.1.1 → mycli-2.3.0}/mycli/clitoolbar.py +0 -0
  76. {mycli-2.1.1 → mycli-2.3.0}/mycli/compat.py +0 -0
  77. {mycli-2.1.1 → mycli-2.3.0}/mycli/config.py +0 -0
  78. {mycli-2.1.1 → mycli-2.3.0}/mycli/constants.py +0 -0
  79. {mycli-2.1.1 → mycli-2.3.0}/mycli/key_bindings.py +0 -0
  80. {mycli-2.1.1 → mycli-2.3.0}/mycli/lexer.py +0 -0
  81. {mycli-2.1.1 → mycli-2.3.0}/mycli/main_modes/batch.py +0 -0
  82. {mycli-2.1.1 → mycli-2.3.0}/mycli/main_modes/checkup.py +0 -0
  83. {mycli-2.1.1 → mycli-2.3.0}/mycli/main_modes/list_dsn.py +0 -0
  84. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/__init__.py +0 -0
  85. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/batch_utils.py +0 -0
  86. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/cli_utils.py +0 -0
  87. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/completion_engine.py +0 -0
  88. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/filepaths.py +0 -0
  89. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/hybrid_redirection.py +0 -0
  90. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/interactive_utils.py +0 -0
  91. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/key_binding_utils.py +0 -0
  92. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  93. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  94. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/ptoolkit/history.py +0 -0
  95. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/ptoolkit/utils.py +0 -0
  96. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/dbcommands.py +0 -0
  97. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/delimitercommand.py +0 -0
  98. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/favoritequeries.py +0 -0
  99. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/special/llm.py +0 -0
  100. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/sql_utils.py +0 -0
  101. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/sqlresult.py +0 -0
  102. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/string_utils.py +0 -0
  103. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/tabular_output/__init__.py +0 -0
  104. {mycli-2.1.1 → mycli-2.3.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  105. {mycli-2.1.1 → mycli-2.3.0}/mycli/sqlcompleter.py +0 -0
  106. {mycli-2.1.1 → mycli-2.3.0}/mycli/types.py +0 -0
  107. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/dependency_links.txt +0 -0
  108. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/entry_points.txt +0 -0
  109. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/requires.txt +0 -0
  110. {mycli-2.1.1 → mycli-2.3.0}/mycli.egg-info/top_level.txt +0 -0
  111. {mycli-2.1.1 → mycli-2.3.0}/pyproject.toml +0 -0
  112. {mycli-2.1.1 → mycli-2.3.0}/setup.cfg +0 -0
  113. {mycli-2.1.1 → mycli-2.3.0}/test/__init__.py +0 -0
  114. {mycli-2.1.1 → mycli-2.3.0}/test/features/__init__.py +0 -0
  115. {mycli-2.1.1 → mycli-2.3.0}/test/features/auto_vertical.feature +0 -0
  116. {mycli-2.1.1 → mycli-2.3.0}/test/features/basic_commands.feature +0 -0
  117. {mycli-2.1.1 → mycli-2.3.0}/test/features/connection.feature +0 -0
  118. {mycli-2.1.1 → mycli-2.3.0}/test/features/crud_database.feature +0 -0
  119. {mycli-2.1.1 → mycli-2.3.0}/test/features/crud_table.feature +0 -0
  120. {mycli-2.1.1 → mycli-2.3.0}/test/features/db_utils.py +0 -0
  121. {mycli-2.1.1 → mycli-2.3.0}/test/features/environment.py +0 -0
  122. {mycli-2.1.1 → mycli-2.3.0}/test/features/fixture_utils.py +0 -0
  123. {mycli-2.1.1 → mycli-2.3.0}/test/features/iocommands.feature +0 -0
  124. {mycli-2.1.1 → mycli-2.3.0}/test/features/named_queries.feature +0 -0
  125. {mycli-2.1.1 → mycli-2.3.0}/test/features/specials.feature +0 -0
  126. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/__init__.py +0 -0
  127. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/auto_vertical.py +0 -0
  128. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/basic_commands.py +0 -0
  129. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/connection.py +0 -0
  130. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/crud_database.py +0 -0
  131. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/crud_table.py +0 -0
  132. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/iocommands.py +0 -0
  133. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/named_queries.py +0 -0
  134. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/specials.py +0 -0
  135. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/utils.py +0 -0
  136. {mycli-2.1.1 → mycli-2.3.0}/test/features/steps/wrappers.py +0 -0
  137. {mycli-2.1.1 → mycli-2.3.0}/test/features/wrappager.py +0 -0
  138. {mycli-2.1.1 → mycli-2.3.0}/test/mylogin.cnf +0 -0
  139. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/conftest.py +0 -0
  140. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_app_state.py +0 -0
  141. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_batch_utils.py +0 -0
  142. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_checkup.py +0 -0
  143. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_cli_utils.py +0 -0
  144. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_clibuffer.py +0 -0
  145. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_client_commands.py +0 -0
  146. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_client_query.py +0 -0
  147. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_clistyle.py +0 -0
  148. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_clitoolbar.py +0 -0
  149. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_completer_use_switch.py +0 -0
  150. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_completion_engine.py +0 -0
  151. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_config.py +0 -0
  152. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_delimitercommand.py +0 -0
  153. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_dsn_aliases.py +0 -0
  154. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_favoritequeries.py +0 -0
  155. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_filepaths.py +0 -0
  156. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_hybrid_redirection.py +0 -0
  157. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_interactive_utils.py +0 -0
  158. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_key_binding_utils.py +0 -0
  159. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_key_bindings.py +0 -0
  160. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_lexer.py +0 -0
  161. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_main_modes_batch.py +0 -0
  162. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_main_modes_execute.py +0 -0
  163. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  164. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_naive_completion.py +0 -0
  165. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  166. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_ptoolkit_history.py +0 -0
  167. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  168. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  169. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_dbcommands.py +0 -0
  170. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_init.py +0 -0
  171. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_llm.py +0 -0
  172. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_special_main.py +0 -0
  173. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_sql_utils.py +0 -0
  174. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_sqlcompleter.py +0 -0
  175. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_sqlresult.py +0 -0
  176. {mycli-2.1.1 → mycli-2.3.0}/test/pytests/test_string_utils.py +0 -0
@@ -24,7 +24,7 @@ jobs:
24
24
  steps:
25
25
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
26
26
 
27
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
27
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
28
28
  with:
29
29
  version: "latest"
30
30
 
@@ -66,7 +66,7 @@ jobs:
66
66
  steps:
67
67
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
68
68
 
69
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
69
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
70
70
  with:
71
71
  version: "latest"
72
72
 
@@ -116,7 +116,7 @@ jobs:
116
116
  steps:
117
117
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
118
118
 
119
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
119
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
120
120
  with:
121
121
  version: 'latest'
122
122
 
@@ -219,7 +219,7 @@ jobs:
219
219
  steps:
220
220
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
221
221
 
222
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
222
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
223
223
  with:
224
224
  version: "latest"
225
225
 
@@ -29,7 +29,7 @@ jobs:
29
29
 
30
30
  steps:
31
31
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
32
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
33
33
  with:
34
34
  version: "latest"
35
35
 
@@ -74,7 +74,7 @@ jobs:
74
74
  steps:
75
75
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
76
76
 
77
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
77
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
78
78
  with:
79
79
  version: "latest"
80
80
 
@@ -124,7 +124,7 @@ jobs:
124
124
  steps:
125
125
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
126
126
 
127
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
127
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
128
128
  with:
129
129
  version: 'latest'
130
130
 
@@ -226,7 +226,7 @@ jobs:
226
226
 
227
227
  steps:
228
228
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
229
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
229
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
230
230
  with:
231
231
  version: "latest"
232
232
 
@@ -25,7 +25,7 @@ jobs:
25
25
  with:
26
26
  python-version: '3.14'
27
27
 
28
- - uses: astral-sh/setup-uv@f98e06938123ccabd21905ea5d0069192241f9f1 # v8.3.1
28
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
29
29
  with:
30
30
  version: 'latest'
31
31
 
@@ -27,8 +27,11 @@ A command line client for MySQL with auto-completion and syntax highlighting.
27
27
  ├── mycli/lexer.py # extends `MySqlLexer` from Pygments
28
28
  ├── mycli/main.py # processes CLI arguments
29
29
  ├── mycli/main_modes/ # main execution paths
30
- ├── mycli/main_modes/batch.py # batch mode execution path
31
- ├── mycli/main_modes/checkup.py # implementation of `--checkup` mode
30
+ ├── mycli/main_modes/batch.py # `--batch` mode
31
+ ├── mycli/main_modes/checkup.py # `--checkup` mode
32
+ ├── mycli/main_modes/execute.py # `--execute` mode
33
+ ├── mycli/main_modes/list_dsn.py # `--list-dsn` mode
34
+ ├── mycli/main_modes/repl.py # interactive REPL mode
32
35
  ├── mycli/myclirc # project-level configuration file
33
36
  ├── mycli/output.py # `OutputMixin` mixin for feedback and output of query results
34
37
  ├── mycli/packages/ # application packages
@@ -48,13 +51,14 @@ A command line client for MySQL with auto-completion and syntax highlighting.
48
51
  ├── mycli/schema_prefetcher.py # background prefetcher for multi-schema auto-completion
49
52
  ├── mycli/sqlcompleter.py # offers SQL completions
50
53
  ├── mycli/sqlexecute.py # runs SQL queries
51
- ├── test/conftest.py # pytest configuration
54
+ ├── mycli/types.py # shared types
55
+ ├── mycli/vault.py # Vault integration
52
56
  ├── test/features/ # behave tests
53
57
  ├── test/myclirc # mycli configuration used for tests
54
58
  ├── test/mylogin.cnf # `mylogin.cnf` example used for tests
55
59
  ├── test/pytests/ # pytest tests
56
- ├── test/utils.py # shared utilities for tests
57
- └── types.py # shared types
60
+ ├── test/pytests/conftest.py # pytest configuration
61
+ └── test/utils.py # shared utilities for tests
58
62
 
59
63
  ## Development
60
64
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.1.1
3
+ Version: 2.3.0
4
4
  Summary: CLI for MySQL Database. With auto-completion and syntax highlighting.
5
5
  Author: Mycli Core Team
6
6
  License-Expression: BSD-3-Clause
@@ -89,13 +89,13 @@ You might need `sudo` on Linux.
89
89
  pip install --upgrade 'mycli[all]'
90
90
  ```
91
91
 
92
- or, only on macOS (`fzf` and `pygments` are optional):
92
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
93
93
 
94
94
  ```bash
95
95
  brew update && brew install mycli fzf pygments
96
96
  ```
97
97
 
98
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
98
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
99
99
 
100
100
  ```bash
101
101
  sudo apt-get install mycli fzf python3-pygments
@@ -115,6 +115,7 @@ Features
115
115
  * Auto-completion as you type for SQL keywords as well as tables, views,
116
116
  columns, enums, and more!
117
117
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
118
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
118
119
  * Syntax highlighting using [Pygments](https://pygments.org/).
119
120
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
120
121
  - `SELECT * FROM <tab>` will only show table names.
@@ -158,15 +159,15 @@ These are some alternative ways to install mycli that are not managed by our
158
159
  team but provided by OS package maintainers. OS packages could be somewhat
159
160
  out of date.
160
161
 
161
- If present, the `fzf` package can be used for fuzzy history search, and
162
- `pygemtize` can be used for syntax highlighting within the fuzzy history
163
- search. The `less` package is also expected, but almost always already
164
- installed.
162
+ If present, the `fzf` package can be used for fuzzy history search, and as an
163
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
164
+ syntax highlighting within the fuzzy history search. The `less` package is
165
+ also expected, but almost always already installed.
165
166
 
166
167
  ### Arch, Manjaro
167
168
 
168
169
  You can install the `mycli` package available in the AUR. `fzf` and
169
- `python-pygments` are optional:
170
+ `python-pygments` are optional but recommended:
170
171
 
171
172
  ```bash
172
173
  yay -S mycli fzf python-pygments
@@ -175,7 +176,8 @@ yay -S mycli fzf python-pygments
175
176
  ### Debian, Ubuntu
176
177
 
177
178
  On Debian and Ubuntu distributions, you can easily install the mycli package
178
- using apt. The `fzf` and `python3-pygments` packages are optional:
179
+ using apt. The `fzf` and `python3-pygments` packages are optional but
180
+ recommended:
179
181
 
180
182
  ```bash
181
183
  sudo apt-get install mycli fzf python3-pygments
@@ -184,7 +186,7 @@ sudo apt-get install mycli fzf python3-pygments
184
186
  ### Fedora
185
187
 
186
188
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
187
- `python-pygments` packages are optional:
189
+ `python-pygments` packages are optional but recommended:
188
190
 
189
191
  ```bash
190
192
  sudo dnf install mycli fzf python-pygments
@@ -196,7 +198,9 @@ sudo dnf install mycli fzf python-pygments
196
198
 
197
199
  Install the `less` pager, for example by `scoop install less`.
198
200
 
199
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
201
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
202
+
203
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
200
204
 
201
205
  The libraries used in mycli are Windows-compatible, but there are known
202
206
  limitations according to the test suite. The basics work without any
@@ -209,11 +213,24 @@ PRs to address shortcomings on Windows would be welcome!
209
213
  Mycli is more compatible with WSL than with native Windows, though still
210
214
  not 100% perfect. This is a good option for using mycli on Windows.
211
215
 
216
+ With WSL, you are probably using an Ubuntu distribution, in which case
217
+ recommended dependencies can be installed by
218
+
219
+ ```bash
220
+ sudo apt-get install fzf python3-pygments
221
+ ```
222
+
223
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
224
+
225
+ ```bash
226
+ pip install --upgrade 'mycli[all]'
227
+ ```
228
+
212
229
  PRs to complete WSL support would be welcome!
213
230
 
214
231
  ### Thanks
215
232
 
216
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
233
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
217
234
 
218
235
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
219
236
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -241,7 +258,7 @@ following in `~/.myclirc`:
241
258
  default_character_set = utf8
242
259
  ```
243
260
 
244
- or set `--charset=utf8` when invoking MyCLI.
261
+ or set `--charset=utf8` when invoking mycli
245
262
 
246
263
  ### Configuration and Usage
247
264
 
@@ -32,13 +32,13 @@ You might need `sudo` on Linux.
32
32
  pip install --upgrade 'mycli[all]'
33
33
  ```
34
34
 
35
- or, only on macOS (`fzf` and `pygments` are optional):
35
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
36
36
 
37
37
  ```bash
38
38
  brew update && brew install mycli fzf pygments
39
39
  ```
40
40
 
41
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
41
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
42
42
 
43
43
  ```bash
44
44
  sudo apt-get install mycli fzf python3-pygments
@@ -58,6 +58,7 @@ Features
58
58
  * Auto-completion as you type for SQL keywords as well as tables, views,
59
59
  columns, enums, and more!
60
60
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
61
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
61
62
  * Syntax highlighting using [Pygments](https://pygments.org/).
62
63
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
63
64
  - `SELECT * FROM <tab>` will only show table names.
@@ -101,15 +102,15 @@ These are some alternative ways to install mycli that are not managed by our
101
102
  team but provided by OS package maintainers. OS packages could be somewhat
102
103
  out of date.
103
104
 
104
- If present, the `fzf` package can be used for fuzzy history search, and
105
- `pygemtize` can be used for syntax highlighting within the fuzzy history
106
- search. The `less` package is also expected, but almost always already
107
- installed.
105
+ If present, the `fzf` package can be used for fuzzy history search, and as an
106
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
107
+ syntax highlighting within the fuzzy history search. The `less` package is
108
+ also expected, but almost always already installed.
108
109
 
109
110
  ### Arch, Manjaro
110
111
 
111
112
  You can install the `mycli` package available in the AUR. `fzf` and
112
- `python-pygments` are optional:
113
+ `python-pygments` are optional but recommended:
113
114
 
114
115
  ```bash
115
116
  yay -S mycli fzf python-pygments
@@ -118,7 +119,8 @@ yay -S mycli fzf python-pygments
118
119
  ### Debian, Ubuntu
119
120
 
120
121
  On Debian and Ubuntu distributions, you can easily install the mycli package
121
- using apt. The `fzf` and `python3-pygments` packages are optional:
122
+ using apt. The `fzf` and `python3-pygments` packages are optional but
123
+ recommended:
122
124
 
123
125
  ```bash
124
126
  sudo apt-get install mycli fzf python3-pygments
@@ -127,7 +129,7 @@ sudo apt-get install mycli fzf python3-pygments
127
129
  ### Fedora
128
130
 
129
131
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
130
- `python-pygments` packages are optional:
132
+ `python-pygments` packages are optional but recommended:
131
133
 
132
134
  ```bash
133
135
  sudo dnf install mycli fzf python-pygments
@@ -139,7 +141,9 @@ sudo dnf install mycli fzf python-pygments
139
141
 
140
142
  Install the `less` pager, for example by `scoop install less`.
141
143
 
142
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
144
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
145
+
146
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
143
147
 
144
148
  The libraries used in mycli are Windows-compatible, but there are known
145
149
  limitations according to the test suite. The basics work without any
@@ -152,11 +156,24 @@ PRs to address shortcomings on Windows would be welcome!
152
156
  Mycli is more compatible with WSL than with native Windows, though still
153
157
  not 100% perfect. This is a good option for using mycli on Windows.
154
158
 
159
+ With WSL, you are probably using an Ubuntu distribution, in which case
160
+ recommended dependencies can be installed by
161
+
162
+ ```bash
163
+ sudo apt-get install fzf python3-pygments
164
+ ```
165
+
166
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
167
+
168
+ ```bash
169
+ pip install --upgrade 'mycli[all]'
170
+ ```
171
+
155
172
  PRs to complete WSL support would be welcome!
156
173
 
157
174
  ### Thanks
158
175
 
159
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
176
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
160
177
 
161
178
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
162
179
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -184,7 +201,7 @@ following in `~/.myclirc`:
184
201
  default_character_set = utf8
185
202
  ```
186
203
 
187
- or set `--charset=utf8` when invoking MyCLI.
204
+ or set `--charset=utf8` when invoking mycli
188
205
 
189
206
  ### Configuration and Usage
190
207
 
@@ -1,3 +1,55 @@
1
+ 2.3.0 (2026/07/16)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Set minimum times for transient toolbar messages.
7
+ * `editor_command` entry in `~/.myclirc` which overrides environment.
8
+ * Experimental: let Vault properties be set in DSNs.
9
+ * Add a `\x` special command to send output to an explorer rather than a pager.
10
+
11
+
12
+ Documentation
13
+ ---------
14
+ * Spellcheck myclirc commentary.
15
+ * Better ordering for `/help` table.
16
+
17
+
18
+ 2.2.0 (2026/07/11)
19
+ ==============
20
+
21
+ Breaking Changes
22
+ ---------
23
+ * `ssl` is no longer supported as a DSN parameter. Use `ssl_mode` instead.
24
+
25
+
26
+ Features
27
+ ---------
28
+ * Configurable tunnel method for `--ssh-jump`, making UDS usually default.
29
+ * Add `--ssh-options` CLI argument to pass extra options with `--ssh-jump`.
30
+ * Make `ssh_jump` a DSN query parameter.
31
+ * Warn on unknown DSN query parameters.
32
+ * Experimental: reading credentials from Vault using the `vault kv get` CLI.
33
+
34
+
35
+ Bug Fixes
36
+ ---------
37
+ * Allow `/dsn` command to show correct values with `--ssh-jump`.
38
+
39
+
40
+ Documentation
41
+ ---------
42
+ * Add `--ssh-jump` to TIPS.
43
+ * Better document `/dsn show`.
44
+ * Improve `ssh_options` commentary.
45
+
46
+
47
+ Internal
48
+ ---------
49
+ * Make flaky `/watch` test more lenient for CI.
50
+ * Tweak `AGENTS.md` project structure.
51
+
52
+
1
53
  2.1.1 (2026/07/08)
2
54
  ==============
3
55
 
@@ -40,6 +40,8 @@ the --login-path option lets you work with login-path files!
40
40
 
41
41
  --keepalive-ticks=<num> sets keepalive pings for a single session!
42
42
 
43
+ connect via an SSH tunnel with --ssh-jump!
44
+
43
45
  ###
44
46
  ### commands
45
47
  ###
@@ -136,7 +138,9 @@ set environment variable MYSQL_DSN to set a default DSN!
136
138
  ### general
137
139
  ###
138
140
 
139
- display query output vertically using \G at the end of a query!
141
+ display output vertically using \G at the end of a query!
142
+
143
+ display output in an interactive explorer \x at the end of a query!
140
144
 
141
145
  run SQL scripts in batch mode using the standard input!
142
146
 
@@ -280,6 +284,10 @@ use "min_completion_trigger" in ~/.myclirc to defer completions!
280
284
 
281
285
  colorize search previews with "highlight_preview" in ~/.myclirc!
282
286
 
287
+ set editor_command = "code --wait" in ~/.myclirc to edit queries using VS Code!
288
+
289
+ set up an interactive output explorer using the "[explorer]" section of ~/.myclirc!
290
+
283
291
  ###
284
292
  ### redirection
285
293
  ###
@@ -9,18 +9,44 @@ from urllib.parse import parse_qs, unquote, urlparse
9
9
  import click
10
10
 
11
11
  from mycli.config import str_to_bool
12
- from mycli.constants import EMPTY_PASSWORD_FLAG_SENTINEL, ISSUES_URL
12
+ from mycli.constants import EMPTY_PASSWORD_FLAG_SENTINEL
13
13
  from mycli.main_modes.batch import main_batch_from_stdin, main_batch_with_progress_bar, main_batch_without_progress_bar
14
14
  from mycli.main_modes.checkup import main_checkup
15
15
  from mycli.main_modes.execute import main_execute_from_cli
16
16
  from mycli.main_modes.list_dsn import main_list_dsn
17
17
  from mycli.packages.cli_utils import is_valid_connection_scheme
18
+ from mycli.vault import (
19
+ DEFAULT_VAULT_EXECUTABLE,
20
+ DEFAULT_VAULT_PASSWORD_FIELD,
21
+ DEFAULT_VAULT_USERNAME_FIELD,
22
+ VaultError,
23
+ get_field_from_vault,
24
+ )
18
25
 
19
26
  if TYPE_CHECKING:
20
27
  from mycli.main import CliArgs
21
28
 
22
29
  ClientFactory = Callable[..., Any]
23
30
  ENV_VAR_PATTERN = re.compile(r'^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$')
31
+ KNOWN_DSN_QUERY_PARAMS = {
32
+ 'character_set',
33
+ 'keepalive_ticks',
34
+ 'socket',
35
+ 'ssh_jump',
36
+ 'ssl_ca',
37
+ 'ssl_capath',
38
+ 'ssl_cert',
39
+ 'ssl_cipher',
40
+ 'ssl_key',
41
+ 'ssl_mode',
42
+ 'ssl_verify_server_cert',
43
+ 'tls_version',
44
+ 'vault_address',
45
+ 'vault_mount',
46
+ 'vault_secret',
47
+ 'vault_password_field',
48
+ 'vault_username_field',
49
+ }
24
50
 
25
51
 
26
52
  class DsnAliasEnvVarError(ValueError):
@@ -79,7 +105,10 @@ def expand_dsn_alias_env_vars(
79
105
  except ValueError as exc:
80
106
  raise DsnAliasEnvVarError(f'Port in DSN alias {alias_name} must be an integer.') from exc
81
107
 
82
- params = {key: [expand_dsn_alias_env_var(value, alias_name) or '' for value in values] for key, values in parse_qs(uri.query).items()}
108
+ params = {
109
+ key: [expand_dsn_alias_env_var(value, alias_name) or '' for value in values]
110
+ for key, values in parse_qs(uri.query, keep_blank_values=True).items()
111
+ }
83
112
 
84
113
  return (
85
114
  expand_dsn_alias_env_var(unquote(username) if username is not None else None, alias_name),
@@ -200,7 +229,7 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
200
229
  dsn_host = uri.hostname
201
230
  dsn_port = uri.port
202
231
  dsn_database = uri.path[1:]
203
- dsn_params = parse_qs(uri.query) if uri.query else {}
232
+ dsn_params = parse_qs(uri.query, keep_blank_values=True) if uri.query else {}
204
233
 
205
234
  if not database:
206
235
  database = dsn_database
@@ -214,16 +243,13 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
214
243
  if not cli_args.port:
215
244
  cli_args.port = dsn_port
216
245
 
217
- if params := dsn_params.get('ssl'):
246
+ if unknown_dsn_params := sorted(set(dsn_params) - KNOWN_DSN_QUERY_PARAMS):
218
247
  click.secho(
219
- 'Warning: The "ssl" DSN URI parameter is deprecated and will be removed in a future release. '
220
- 'Please use the "ssl_mode" parameter instead. '
221
- f'See issue {ISSUES_URL}/1507',
248
+ f'Warning: Ignored unknown DSN URI query parameters: {", ".join(unknown_dsn_params)}.',
222
249
  err=True,
223
250
  fg='yellow',
224
251
  )
225
- if params[0].lower() == 'true':
226
- cli_args.ssl_mode = 'on'
252
+
227
253
  if params := dsn_params.get('ssl_mode'):
228
254
  cli_args.ssl_mode = cli_args.ssl_mode or params[0]
229
255
  if params := dsn_params.get('ssl_ca'):
@@ -254,6 +280,18 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
254
280
  cli_args.keepalive_ticks = int(params[0])
255
281
  if params := dsn_params.get('character_set'):
256
282
  cli_args.character_set = cli_args.character_set or params[0]
283
+ if params := dsn_params.get('ssh_jump'):
284
+ cli_args.ssh_jump = cli_args.ssh_jump or params[0]
285
+ if params := dsn_params.get('vault_address'):
286
+ cli_args.vault_address = cli_args.vault_address or params[0]
287
+ if params := dsn_params.get('vault_mount'):
288
+ cli_args.vault_mount = cli_args.vault_mount or params[0]
289
+ if params := dsn_params.get('vault_secret'):
290
+ cli_args.vault_secret = cli_args.vault_secret or params[0]
291
+ if params := dsn_params.get('vault_password_field'):
292
+ cli_args.vault_password_field = cli_args.vault_password_field or params[0]
293
+ if params := dsn_params.get('vault_username_field'):
294
+ cli_args.vault_username_field = cli_args.vault_username_field or params[0]
257
295
 
258
296
  keepalive_ticks = cli_args.keepalive_ticks if cli_args.keepalive_ticks is not None else mycli.default_keepalive_ticks
259
297
  ssl_mode = cli_args.ssl_mode or mycli.ssl_mode
@@ -326,11 +364,52 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
326
364
  use_keyring = str_to_bool(cli_args.use_keyring)
327
365
  reset_keyring = False
328
366
 
367
+ if cli_args.password is None and cli_args.vault_secret:
368
+ vault_config = mycli.config.get('vault_beta', {})
369
+ vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
370
+ vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
371
+ vault_field = cli_args.vault_password_field or vault_config.get('default_password_field') or DEFAULT_VAULT_PASSWORD_FIELD
372
+ vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
373
+ try:
374
+ vault_password: str | None = get_field_from_vault(
375
+ vault_field,
376
+ cli_args.vault_secret,
377
+ executable=vault_executable,
378
+ mount=vault_mount,
379
+ address=vault_address,
380
+ )
381
+ except VaultError as exc:
382
+ click.secho(f'Error reading password from Vault: {exc}', err=True, fg='red')
383
+ sys.exit(1)
384
+ else:
385
+ vault_password = None
386
+
387
+ if cli_args.user is None and cli_args.vault_secret:
388
+ vault_config = mycli.config.get('vault_beta', {})
389
+ vault_address = cli_args.vault_address or os.environ.get('VAULT_ADDR') or vault_config.get('address') or None
390
+ vault_mount = cli_args.vault_mount or vault_config.get('default_mount') or None
391
+ vault_field = cli_args.vault_username_field or vault_config.get('default_username_field') or DEFAULT_VAULT_USERNAME_FIELD
392
+ vault_executable = vault_config.get('vault_executable') or DEFAULT_VAULT_EXECUTABLE
393
+ try:
394
+ vault_username = get_field_from_vault(
395
+ vault_field,
396
+ cli_args.vault_secret,
397
+ executable=vault_executable,
398
+ mount=vault_mount,
399
+ address=vault_address,
400
+ )
401
+ except VaultError as exc:
402
+ click.secho(f'Error reading username from Vault: {exc}', err=True, fg='red')
403
+ sys.exit(1)
404
+ else:
405
+ vault_username = None
406
+ vault_username_from_vault = vault_username is not None
407
+
329
408
  try:
330
409
  mycli.connect(
331
410
  database=database,
332
- user=cli_args.user,
333
- passwd=cli_args.password,
411
+ user=vault_username if cli_args.user is None else cli_args.user,
412
+ passwd=vault_password if cli_args.password is None else cli_args.password,
334
413
  host=cli_args.host,
335
414
  port=cli_args.port,
336
415
  socket=cli_args.socket,
@@ -343,6 +422,13 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
343
422
  reset_keyring=reset_keyring,
344
423
  keepalive_ticks=keepalive_ticks,
345
424
  ssh_jump=cli_args.ssh_jump,
425
+ ssh_cli_options=cli_args.ssh_options,
426
+ vault_address=cli_args.vault_address,
427
+ vault_mount=cli_args.vault_mount,
428
+ vault_secret=cli_args.vault_secret,
429
+ vault_password_field=cli_args.vault_password_field,
430
+ vault_username_field=cli_args.vault_username_field,
431
+ vault_username_from_vault=vault_username_from_vault,
346
432
  )
347
433
 
348
434
  if combined_init_cmd:
@@ -42,6 +42,7 @@ def _multiline_exception(text: str) -> bool:
42
42
  # or ended with certain commands
43
43
  "\\g",
44
44
  "\\G",
45
+ "\\x",
45
46
  r"\e",
46
47
  r"\edit",
47
48
  r"\clip",
@@ -114,10 +114,15 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
114
114
  self.dsn_alias: str | None = None
115
115
  self.main_formatter = TabularOutputFormatter(format_name=c["main"]["table_format"])
116
116
  self.redirect_formatter = TabularOutputFormatter(format_name=c["main"].get("redirect_format", "csv"))
117
+ self.explorer_formatter = TabularOutputFormatter(format_name=c['explorer'].get('format', self.main_formatter))
117
118
  sql_format.register_new_formatter(self.main_formatter)
118
119
  sql_format.register_new_formatter(self.redirect_formatter)
120
+ sql_format.register_new_formatter(self.explorer_formatter)
119
121
  self.main_formatter.mycli = self
120
122
  self.redirect_formatter.mycli = self
123
+ self.explorer_formatter.mycli = self
124
+ self.explorer_command = c['explorer'].get('explorer_command')
125
+ self.explorer_trim_footer = c['explorer'].as_bool('trim_footer')
121
126
  self.syntax_style = c["main"]["syntax_style"]
122
127
  self.verbosity = -1 if c["main"].as_bool("less_chatty") else 0
123
128
  if cli_verbosity:
@@ -155,7 +160,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
155
160
  self.echo("Error: Unable to open the audit log file. Your queries will not be logged.", err=True, fg="red")
156
161
  self.logfile = False
157
162
 
158
- self.completion_refresher = CompletionRefresher()
163
+ self.completion_refresher = CompletionRefresher(self._invalidate_prompt_session)
159
164
  self.prefetch_schemas_mode = c["main"].get("prefetch_schemas_mode", "always") or "always"
160
165
  raw_prefetch_list = c["main"].as_list("prefetch_schemas_list") if "prefetch_schemas_list" in c["main"] else []
161
166
  self.prefetch_schemas_list = [s.strip() for s in raw_prefetch_list if s and s.strip()]
@@ -201,6 +206,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
201
206
  self.destructive_keywords = destructive_keywords_from_config(c)
202
207
  special.set_destructive_keywords(self.destructive_keywords)
203
208
 
209
+ def _invalidate_prompt_session(self) -> None:
210
+ if self.prompt_session:
211
+ self.prompt_session.app.invalidate()
212
+
204
213
  def close(self) -> None:
205
214
  try:
206
215
  self.schema_prefetcher.stop()