From f23d58f465b5f83d1bed0db736ddb3a1808cf5b4 Mon Sep 17 00:00:00 2001 From: Scott Nemes Date: Sun, 4 Jan 2026 18:10:49 -0800 Subject: [PATCH 1/5] Updated watch command code to show the correct execution time on all iterrations. --- changelog.md | 8 ++++++++ mycli/main.py | 10 ++++++++++ mycli/packages/special/iocommands.py | 5 +++-- test/test_special_iocommands.py | 4 ++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 56a19096..ec8ab05a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +Upcoming (TBD) +============== + +Bug Fixes +-------- +* Update watch query output to display the correct execution time on all iterations (#763). + + 1.43.0 (2026/01/02) ============== diff --git a/mycli/main.py b/mycli/main.py index c8d74af5..0511e8be 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -797,6 +797,13 @@ def output_res(res: Generator[tuple], start: float) -> None: logger.debug("rows: %r", cur) logger.debug("status: %r", status) threshold = 1000 + # If this is a watch query, offset the start time on the 2nd+ iteration + # to account for the sleep duration and reset the title back to the query + if title is not None and title[0] == "watch": + watch_seconds = float(title[1]) + if (time() - start) >= watch_seconds: + start += watch_seconds + title = title[2] if is_select(status) and cur and cur.rowcount > threshold: self.echo( f"The result set has more than {threshold} rows.", @@ -1310,6 +1317,9 @@ def run_query(self, query: str, new_line: bool = True) -> None: title, cur, headers, status = result self.main_formatter.query = query self.redirect_formatter.query = query + # If this is a watch query, reset the title back to the query + if title is not None and title[0] == "watch": + title = title[2] output = self.format_output( title, cur, diff --git a/mycli/packages/special/iocommands.py b/mycli/packages/special/iocommands.py index 3304ee2a..67d4ab94 100644 --- a/mycli/packages/special/iocommands.py +++ b/mycli/packages/special/iocommands.py @@ -566,11 +566,12 @@ def watch_query(arg: str, **kwargs) -> Generator[tuple, None, None]: set_pager_enabled(False) for sql, title in sql_list: cur.execute(sql) + overloaded_title = ["watch", seconds, title] if cur.description: headers = [x[0] for x in cur.description] - yield (title, cur, headers, None) + yield (overloaded_title, cur, headers, None) else: - yield (title, None, None, None) + yield (overloaded_title, None, None, None) sleep(seconds) except KeyboardInterrupt: # This prints the Ctrl-C character in its own line, which prevents diff --git a/test/test_special_iocommands.py b/test/test_special_iocommands.py index 1a738484..af8ebb04 100644 --- a/test/test_special_iocommands.py +++ b/test/test_special_iocommands.py @@ -201,7 +201,7 @@ def test_watch_query_iteration(): expected_title = f"> {query}" with db_connection().cursor() as cur: result = next(mycli.packages.special.iocommands.watch_query(arg=query, cur=cur)) - assert result[0] == expected_title + assert result[0][2] == expected_title # for watch title is overloaded ["watch", seconds, title] assert result[2][0] == expected_value @@ -229,7 +229,7 @@ def test_watch_query_full(): ctrl_c_process.join(1) assert len(results) in expected_results for result in results: - assert result[0] == expected_title + assert result[0][2] == expected_title # for watch title is overloaded ["watch", seconds, title] assert result[2][0] == expected_value From a748612d9cb887cc213ff303e6184791e6fbb19f Mon Sep 17 00:00:00 2001 From: Scott Nemes Date: Mon, 5 Jan 2026 20:47:52 -0800 Subject: [PATCH 2/5] Moved the command args to the results tuple instead of overloading the title --- mycli/main.py | 28 ++++++++++++++++++---------- mycli/packages/special/iocommands.py | 10 +++++++--- test/test_special_iocommands.py | 4 ++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/mycli/main.py b/mycli/main.py index 0511e8be..9305e44c 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -789,21 +789,30 @@ def show_suggestion_tip() -> bool: # mutating if any one of the component statements is mutating mutating = False - def output_res(res: Generator[tuple], start: float) -> None: + def output_res(results: Generator[tuple], start: float) -> None: nonlocal mutating result_count = 0 - for title, cur, headers, status in res: + for result in results: + # unpack the results, including the command info if present + try: + title, cur, headers, status, command = result + except ValueError: + title, cur, headers, status = result + command = None + except Exception as e: + self.echo("An unexpected error has occurred.", err=True, fg="red") + self.logger.error(f"Error unpacking results: {e}") + sys.exit(1) logger.debug("headers: %r", headers) logger.debug("rows: %r", cur) logger.debug("status: %r", status) threshold = 1000 # If this is a watch query, offset the start time on the 2nd+ iteration - # to account for the sleep duration and reset the title back to the query - if title is not None and title[0] == "watch": - watch_seconds = float(title[1]) + # to account for the sleep duration + if command is not None and command["type"] == "command" and command["name"] == "watch": + watch_seconds = float(command["seconds"]) if (time() - start) >= watch_seconds: start += watch_seconds - title = title[2] if is_select(status) and cur and cur.rowcount > threshold: self.echo( f"The result set has more than {threshold} rows.", @@ -1314,12 +1323,11 @@ def run_query(self, query: str, new_line: bool = True) -> None: assert self.sqlexecute is not None results = self.sqlexecute.run(query) for result in results: - title, cur, headers, status = result + # discard the optional command portion of the results + # tuple since it is not used here currently + title, cur, headers, status, *_ = result self.main_formatter.query = query self.redirect_formatter.query = query - # If this is a watch query, reset the title back to the query - if title is not None and title[0] == "watch": - title = title[2] output = self.format_output( title, cur, diff --git a/mycli/packages/special/iocommands.py b/mycli/packages/special/iocommands.py index 67d4ab94..91b8b9f0 100644 --- a/mycli/packages/special/iocommands.py +++ b/mycli/packages/special/iocommands.py @@ -566,12 +566,16 @@ def watch_query(arg: str, **kwargs) -> Generator[tuple, None, None]: set_pager_enabled(False) for sql, title in sql_list: cur.execute(sql) - overloaded_title = ["watch", seconds, title] + command = { + "type": "command", + "name": "watch", + "seconds": seconds, + } if cur.description: headers = [x[0] for x in cur.description] - yield (overloaded_title, cur, headers, None) + yield (title, cur, headers, None, command) else: - yield (overloaded_title, None, None, None) + yield (title, None, None, None, command) sleep(seconds) except KeyboardInterrupt: # This prints the Ctrl-C character in its own line, which prevents diff --git a/test/test_special_iocommands.py b/test/test_special_iocommands.py index af8ebb04..1a738484 100644 --- a/test/test_special_iocommands.py +++ b/test/test_special_iocommands.py @@ -201,7 +201,7 @@ def test_watch_query_iteration(): expected_title = f"> {query}" with db_connection().cursor() as cur: result = next(mycli.packages.special.iocommands.watch_query(arg=query, cur=cur)) - assert result[0][2] == expected_title # for watch title is overloaded ["watch", seconds, title] + assert result[0] == expected_title assert result[2][0] == expected_value @@ -229,7 +229,7 @@ def test_watch_query_full(): ctrl_c_process.join(1) assert len(results) in expected_results for result in results: - assert result[0][2] == expected_title # for watch title is overloaded ["watch", seconds, title] + assert result[0] == expected_title assert result[2][0] == expected_value From fe81ae77f6abec09587193e178f53638aad9a12b Mon Sep 17 00:00:00 2001 From: Scott Nemes Date: Mon, 5 Jan 2026 21:12:38 -0800 Subject: [PATCH 3/5] Improved watch time update logic. --- mycli/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mycli/main.py b/mycli/main.py index 114754fc..8353bcdd 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -839,7 +839,7 @@ def output_res(results: Generator[tuple], start: float) -> None: # to account for the sleep duration if command is not None and command["type"] == "command" and command["name"] == "watch": watch_seconds = float(command["seconds"]) - if (time() - start) >= watch_seconds: + if result_count > 0: start += watch_seconds if is_select(status) and cur and cur.rowcount > threshold: self.echo( From 74f2df075a0a62b51eb0e3cddf98f9d52dff7984 Mon Sep 17 00:00:00 2001 From: Scott Nemes Date: Mon, 5 Jan 2026 21:30:55 -0800 Subject: [PATCH 4/5] Updated extra command logic --- mycli/main.py | 11 +++++++++-- mycli/packages/special/iocommands.py | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mycli/main.py b/mycli/main.py index 8353bcdd..c96e16f8 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -837,7 +837,7 @@ def output_res(results: Generator[tuple], start: float) -> None: threshold = 1000 # If this is a watch query, offset the start time on the 2nd+ iteration # to account for the sleep duration - if command is not None and command["type"] == "command" and command["name"] == "watch": + if command is not None and command["name"] == "watch": watch_seconds = float(command["seconds"]) if result_count > 0: start += watch_seconds @@ -1353,7 +1353,14 @@ def run_query(self, query: str, new_line: bool = True) -> None: for result in results: # discard the optional command portion of the results # tuple since it is not used here currently - title, cur, headers, status, *_ = result + try: + title, cur, headers, status, _ = result + except ValueError: + title, cur, headers, status = result + except Exception as e: + self.echo("An unexpected error has occurred.", err=True, fg="red") + self.logger.error(f"Error unpacking results: {e}") + sys.exit(1) self.main_formatter.query = query self.redirect_formatter.query = query output = self.format_output( diff --git a/mycli/packages/special/iocommands.py b/mycli/packages/special/iocommands.py index 91b8b9f0..2367147a 100644 --- a/mycli/packages/special/iocommands.py +++ b/mycli/packages/special/iocommands.py @@ -567,7 +567,6 @@ def watch_query(arg: str, **kwargs) -> Generator[tuple, None, None]: for sql, title in sql_list: cur.execute(sql) command = { - "type": "command", "name": "watch", "seconds": seconds, } From 1a176daf9bc2b82ea93f05f68ae85745f505f7e9 Mon Sep 17 00:00:00 2001 From: Scott Nemes Date: Sat, 10 Jan 2026 13:44:28 -0800 Subject: [PATCH 5/5] Fixed changelog --- changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index f42a268b..703ec54d 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ Internal -------- * Create new data class to handle SQL/command results to make further code improvements easier +Bug Fixes +-------- +* Update watch query output to display the correct execution time on all iterations (#763). + 1.44.1 (2026/01/10) ============== @@ -22,10 +26,6 @@ Features * Add enum value completions for WHERE/HAVING clauses. (#790) * Add `show_favorite_query` config option to control query printing when running favorite queries. (#1118) -Bug Fixes --------- -* Update watch query output to display the correct execution time on all iterations (#763). - 1.43.1 (2026/01/03) ==============