From ef40d4ab49c597cef282dcce816e3009b223bac9 Mon Sep 17 00:00:00 2001 From: Khaled Riyad Date: Thu, 3 Sep 2026 18:42:15 +0300 Subject: [PATCH] MDEV-40551 Copy/Paste friendly output format for MariaDB Command Line Client Copy/paste friendly output was only reachable by starting the client with --silent --skip-column-names, which cannot be done from a running interactive session. Add \S, a statement terminator which prints the result of one statement in the tab separated format without column names. com_silent() sets output_plain, opt_silent and column_names around com_go(), then restores them, the same way com_ego() handles vertical. output_plain selects print_tab_data() ahead of the vertical and table branches, so \S gives the same output whether the session was started plainly or with --table, --vertical or --silent. --html and --xml still win, matching \G. --- client/mysql.cc | 25 ++++++++++++++++++++++++- mysql-test/main/mysql.result | 35 +++++++++++++++++++++++++++++++++++ mysql-test/main/mysql.test | 29 +++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/client/mysql.cc b/client/mysql.cc index bc2a2bc77cafa..7bc8c36313b94 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -243,6 +243,7 @@ enum ss_comment_type { SSC_NONE= 0, SSC_CONDITIONAL, SSC_HINT }; static MYSQL mysql; /* The connection */ static my_bool ignore_errors=0,wait_flag=0,quick=0, connected=0,opt_raw_data=0,unbuffered=0,output_tables=0, + output_plain=0, opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0, opt_compress=0, using_opt_local_infile=0, vertical=0, line_numbers=1, column_names=1,opt_html=0, @@ -324,7 +325,7 @@ extern "C" my_bool get_one_option(int optid, const struct my_option *opt, const char *argument); static int com_quit(String *str,char*), com_go(String *str,char*), com_ego(String *str,char*), - com_print(String *str,char*), + com_silent(String *str,char*), com_print(String *str,char*), com_help(String *str,char*), com_clear(String *str,char*), com_connect(String *str,char*), com_status(String *str,char*), com_use(String *str,char*), com_source(String *str, char*), @@ -414,6 +415,8 @@ static COMMANDS commands[] = { { "rehash", '#', com_rehash, 0, "Rebuild completion hash." }, { "sandbox", '-', com_sandbox, 0, "Disallow commands that access the file system (except \\P without an argument and \\e)." }, + { "silent", 'S', com_silent, 0, + "Send command to MariaDB server, display result without column names or borders."}, { "source", '.', com_source, 1, "Execute an SQL script file. Takes a file name as an argument."}, { "status", 's', com_status, 0, "Get status information from the server."}, @@ -3739,6 +3742,8 @@ static int com_go(String *buffer, char *) print_table_data_html(result); else if (opt_xml) print_table_data_xml(result); + else if (output_plain) + print_tab_data(result); else if (vertical || (auto_vertical_output && (terminal_width < get_result_width(result)))) print_table_data_vertically(result); @@ -3879,6 +3884,24 @@ com_ego(String *buffer,char *line) } +static int +com_silent(String *buffer,char *line) +{ + int result; + uint old_opt_silent=opt_silent; + bool old_output_plain=output_plain; + bool old_column_names=column_names; + opt_silent=1; + output_plain=1; + column_names=0; + result=com_go(buffer,line); + opt_silent=old_opt_silent; + output_plain=old_output_plain; + column_names=old_column_names; + return result; +} + + static const char *fieldtype2str(enum enum_field_types type) { switch (type) { diff --git a/mysql-test/main/mysql.result b/mysql-test/main/mysql.result index 867e8388fb0d9..029782e3d4f80 100644 --- a/mysql-test/main/mysql.result +++ b/mysql-test/main/mysql.result @@ -55,6 +55,41 @@ _ Test 'go' command g a 1 +_ +Test 'silent' command(no column names) S +a b +1 x +1 x +_ +Test 'silent' command(overrides --table) S +1 x +_ +Test 'silent' command(overrides --vertical) S +1 x +_ +Test 'silent' command(already silent) S +1 x +_ +Test 'silent' command(next statement unaffected) S +1 ++---+ +| a | ++---+ +| 1 | ++---+ +1 +*************************** 1. row *************************** +a: 1 +_ +Test 'silent' command(G unaffected) S +1 +*************************** 1. row *************************** +a: 1 +_ +Test 'silent' command(statement with a quoted delimiter) S +analyze table t1; +_ +Test 'silent' command(empty result and error) S drop table t1; create table t1(a int); lock tables t1 write; diff --git a/mysql-test/main/mysql.test b/mysql-test/main/mysql.test index 3b88b00adf4c9..5f14f0faca5e4 100644 --- a/mysql-test/main/mysql.test +++ b/mysql-test/main/mysql.test @@ -30,6 +30,35 @@ select "Test 'go' command(vertical output) \G" as "_"; # Test 'go' command \g select "Test 'go' command \g" as "_"; --exec $MYSQL test -e "select * from t1\g" +# +# MDEV-40551 Copy/Paste friendly output format for MariaDB Command Line Client +# +# Test 'silent' command \S +select "Test 'silent' command(no column names) \S" as "_"; +--exec $MYSQL test -e "select 1 as a, 'x' as b;" +--exec $MYSQL test -e "select 1 as a, 'x' as b\S" +# \S overrides the session output format, whatever it was started with +select "Test 'silent' command(overrides --table) \S" as "_"; +--exec $MYSQL test --table -e "select 1 as a, 'x' as b\S" +select "Test 'silent' command(overrides --vertical) \S" as "_"; +--exec $MYSQL test --vertical -e "select 1 as a, 'x' as b\S" +select "Test 'silent' command(already silent) \S" as "_"; +--exec $MYSQL test -s -s -e "select 1 as a, 'x' as b\S" +# \S applies to its own statement only +select "Test 'silent' command(next statement unaffected) \S" as "_"; +--exec $MYSQL test --table -e "select 1 as a\S select 1 as a;" +--exec $MYSQL test --vertical -e "select 1 as a\S select 1 as a;" +# \G after \S is still vertical +select "Test 'silent' command(\G unaffected) \S" as "_"; +--exec $MYSQL test --table -e "select 1 as a\S select 1 as a\G" +# The statement from the feature request +select "Test 'silent' command(statement with a quoted delimiter) \S" as "_"; +--exec $MYSQL test --table -e "select concat('analyze table ', table_name, ';') from information_schema.tables where table_schema='test'\S" +# \S on a statement that returns no rows, and on one that fails +select "Test 'silent' command(empty result and error) \S" as "_"; +--exec $MYSQL test --table -e "select 1 as a from t1 where 1=0\S" +--error 1 +--exec $MYSQL test --table -e "select nosuchcolumn from t1\S" --enable_query_log drop table t1;