From 18b0fe7513b4822797981f7c4b18254b9a208ab4 Mon Sep 17 00:00:00 2001 From: David Levy Date: Thu, 5 Feb 2026 20:36:20 -0600 Subject: [PATCH] Fix help flags preprocessing for -h and -help --- cmd/sqlcmd/sqlcmd.go | 27 +++++++++++++++++++++++++++ cmd/sqlcmd/sqlcmd_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 7d69b24b..221393ae 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -294,6 +294,8 @@ func Execute(version string) { // We need to rewrite the arguments to add -i and -v in front of each space-delimited value to be Cobra-friendly. // For flags like -r we need to inject the default value if the user omits it func convertOsArgs(args []string) (cargs []string) { + args = preprocessHelpFlags(args) + flag := "" first := true for i, a := range args { @@ -323,6 +325,31 @@ func convertOsArgs(args []string) (cargs []string) { return } +// preprocessHelpFlags converts -h (without number) and -help to help flags. +// -h with a number is left alone for header count backward compatibility. +func preprocessHelpFlags(args []string) []string { + result := make([]string, 0, len(args)) + for i := 0; i < len(args); i++ { + arg := args[i] + if arg == "-help" { + result = append(result, "--help") + continue + } + if arg == "-h" { + if i+1 < len(args) { + if _, err := strconv.Atoi(args[i+1]); err == nil { + result = append(result, arg) // -h for headers + continue + } + } + result = append(result, "-?") + continue + } + result = append(result, arg) + } + return result +} + // If args[i] is the given flag and args[i+1] is another flag, returns the value to append after the flag func checkDefaultValue(args []string, i int) (val string) { flags := map[rune]string{ diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index 511816b2..4554998a 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -593,6 +593,30 @@ func TestConvertOsArgs(t *testing.T) { } } +func TestPreprocessHelpFlags(t *testing.T) { + type test struct { + name string + in []string + expected []string + } + + tests := []test{ + {"empty args", []string{}, []string{}}, + {"-help to --help", []string{"-help"}, []string{"--help"}}, + {"-h alone to -?", []string{"-h"}, []string{"-?"}}, + {"-h with number stays", []string{"-h", "5"}, []string{"-h", "5"}}, + {"-h with flag becomes -?", []string{"-h", "-S"}, []string{"-?", "-S"}}, + {"mixed args", []string{"-S", "server", "-h", "-Q", "select 1"}, []string{"-S", "server", "-?", "-Q", "select 1"}}, + {"preserve -h N in context", []string{"-S", "srv", "-h", "10", "-Q", "x"}, []string{"-S", "srv", "-h", "10", "-Q", "x"}}, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + actual := preprocessHelpFlags(c.in) + assert.Equal(t, c.expected, actual, "Incorrect preprocessed args") + }) + } +} + func TestEncryptionOptions(t *testing.T) { type test struct { input string