Skip to content

Commit 929c20e

Browse files
committed
stream input from stdin
rather than reading the entire script into memory. * Stream STDIN input, running queries a line at a time. * Remove MemoryError check, and recommendation for the vendor client. * Use CSV/TSV formats with headers for the first line only. * Exit with an error code if we are unable to open /dev/tty. * Add --noninteractive flag to suppress the destructive-warning prompt from the CLI. * Commentary on edge cases and followups.
1 parent 82c7d92 commit 929c20e

File tree

3 files changed

+34
-29
lines changed

3 files changed

+34
-29
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Features
66
* More complete and up-to-date set of MySQL reserved words for completions.
77
* Place exact-leading completions first.
88
* Allow history file location to be configured.
9+
* Stream input to STDIN to consume less memory, and add a `--noninteractive` flag.
910

1011

1112
Bug Fixes

mycli/main.py

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,7 @@ def get_last_query(self) -> str | None:
15541554
"--password-file", type=click.Path(), help="File or FIFO path containing the password to connect to the db if not specified otherwise."
15551555
)
15561556
@click.argument("database", default="", nargs=1)
1557+
@click.option("--noninteractive", is_flag=True, help="Don't prompt during batch input. Recommended.")
15571558
def cli(
15581559
database: str,
15591560
user: str | None,
@@ -1598,6 +1599,7 @@ def cli(
15981599
init_command: str | None,
15991600
charset: str | None,
16001601
password_file: str | None,
1602+
noninteractive: bool,
16011603
) -> None:
16021604
"""A MySQL terminal client with auto-completion and syntax highlighting.
16031605
@@ -1844,36 +1846,36 @@ def cli(
18441846
mycli.run_cli()
18451847
else:
18461848
stdin = click.get_text_stream("stdin")
1847-
try:
1848-
stdin_text = stdin.read()
1849-
except MemoryError:
1850-
click.secho("Failed! Ran out of memory.", err=True, fg="red")
1851-
click.secho("You might want to try the official mysql client.", err=True, fg="red")
1852-
click.secho("Sorry... :(", err=True, fg="red")
1853-
sys.exit(1)
1854-
1855-
if mycli.destructive_warning and is_destructive(stdin_text):
1849+
counter = 0
1850+
for stdin_text in stdin:
1851+
if counter:
1852+
if csv:
1853+
mycli.main_formatter.format_name = "csv-noheader"
1854+
elif not table:
1855+
mycli.main_formatter.format_name = "tsv_noheader"
1856+
else:
1857+
if csv:
1858+
mycli.main_formatter.format_name = "csv"
1859+
elif not table:
1860+
mycli.main_formatter.format_name = "tsv"
1861+
counter += 1
1862+
warn_confirmed = True
1863+
if not noninteractive and mycli.destructive_warning and is_destructive(stdin_text):
1864+
try:
1865+
# this seems to work, even though we are reading from stdin above
1866+
sys.stdin = open("/dev/tty")
1867+
# bug: the prompt will not be visible if stdout is redirected
1868+
warn_confirmed = confirm_destructive_query(stdin_text)
1869+
except (IOError, OSError):
1870+
mycli.logger.warning("Unable to open TTY as stdin.")
1871+
sys.exit(1)
18561872
try:
1857-
sys.stdin = open("/dev/tty")
1858-
warn_confirmed = confirm_destructive_query(stdin_text)
1859-
except (IOError, OSError):
1860-
mycli.logger.warning("Unable to open TTY as stdin.")
1861-
if not warn_confirmed:
1862-
sys.exit(0)
1863-
1864-
try:
1865-
new_line = True
1866-
1867-
if csv:
1868-
mycli.main_formatter.format_name = "csv"
1869-
elif not table:
1870-
mycli.main_formatter.format_name = "tsv"
1871-
1872-
mycli.run_query(stdin_text, new_line=new_line)
1873-
sys.exit(0)
1874-
except Exception as e:
1875-
click.secho(str(e), err=True, fg="red")
1876-
sys.exit(1)
1873+
if warn_confirmed:
1874+
mycli.run_query(stdin_text, new_line=True)
1875+
except Exception as e:
1876+
click.secho(str(e), err=True, fg="red")
1877+
sys.exit(1)
1878+
sys.exit(0)
18771879
mycli.close()
18781880

18791881

mycli/packages/prompt_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def __repr__(self):
2525
BOOLEAN_TYPE = ConfirmBoolParamType()
2626

2727

28+
# todo: all of confirm_destructive_query must be reimplemented in terms
29+
# of /dev/tty, not stdin/stdout.
2830
def confirm_destructive_query(queries: str) -> bool | None:
2931
"""Check if the query is destructive and prompts the user to confirm.
3032

0 commit comments

Comments
 (0)