Skip to content

Commit ff05619

Browse files
Address Copilot review comments for codepage implementation
- Change UTF-16 BOM handling from IgnoreBOM to UseBOM for codepages 1200 (UTF-16 LE) and 1201 (UTF-16 BE) to properly strip BOMs on decode (pkg/sqlcmd/codepage.go) - Eliminate redundant CodePage parsing by storing parsed settings in codePageSettings field after validation in Validate(), then reusing in run() (cmd/sqlcmd/sqlcmd.go) - Add comprehensive Code Page Support documentation section to README.md with format guide, common codepages table, practical examples, and notes on default behavior Note: Integration test for IncludeFile with non-UTF8 input already exists in TestIncludeFileWithInputCodePage (pkg/sqlcmd/sqlcmd_test.go)
1 parent 9b0cd91 commit ff05619

3 files changed

Lines changed: 75 additions & 12 deletions

File tree

README.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ The following switches have different behavior in this version of `sqlcmd` compa
133133
- To provide the value of the host name in the server certificate when using strict encryption, pass the host name with `-F`. Example: `-Ns -F myhost.domain.com`
134134
- More information about client/server encryption negotiation can be found at <https://docs.microsoft.com/openspecs/windows_protocols/ms-tds/60f56408-0188-4cd5-8b90-25c6f2423868>
135135
- `-u` The generated Unicode output file will have the UTF16 Little-Endian Byte-order mark (BOM) written to it.
136-
- `-f` Specifies the code page for input and output files. Format: `codepage | i:codepage[,o:codepage] | o:codepage[,i:codepage]`. Use `65001` for UTF-8. Supported codepages include Unicode (65001, 1200, 1201), Windows (874, 1250-1258), OEM/DOS (437, 850, etc.), ISO-8859 (28591-28606), CJK (932, 936, 949, 950), and EBCDIC (37, 1047, 1140). On Windows, additional codepages installed on the system (such as Japanese EBCDIC) are also available. Use `--list-codepages` to see all supported code pages.
136+
- `-f` Specifies the code page for input and output files. See [Code Page Support](#code-page-support) below for details and examples.
137137
- Some behaviors that were kept to maintain compatibility with `OSQL` may be changed, such as alignment of column headers for some data types.
138138
- All commands must fit on one line, even `EXIT`. Interactive mode will not check for open parentheses or quotes for commands and prompt for successive lines. The ODBC sqlcmd allows the query run by `EXIT(query)` to span multiple lines.
139139
- `-i` doesn't handle a comma `,` in a file name correctly unless the file name argument is triple quoted. For example:
@@ -238,6 +238,68 @@ To see a list of available styles along with colored syntax samples, use this co
238238
:list color
239239
```
240240

241+
### Code Page Support
242+
243+
The `-f` flag specifies the code page for reading input files and writing output. This is useful when working with SQL scripts saved in legacy encodings or when output needs to be in a specific encoding.
244+
245+
#### Format
246+
247+
```
248+
-f codepage # Set both input and output to the same codepage
249+
-f i:codepage # Set input codepage only
250+
-f o:codepage # Set output codepage only
251+
-f i:codepage,o:codepage # Set input and output to different codepages
252+
-f o:codepage,i:codepage # Same as above (order doesn't matter)
253+
```
254+
255+
#### Common Code Pages
256+
257+
| Code Page | Name | Description |
258+
|-----------|------|-------------|
259+
| 65001 | UTF-8 | Unicode (UTF-8) - default for most modern systems |
260+
| 1200 | UTF-16LE | Unicode (UTF-16 Little-Endian) |
261+
| 1201 | UTF-16BE | Unicode (UTF-16 Big-Endian) |
262+
| 1252 | Windows-1252 | Western European (Windows) |
263+
| 932 | Shift_JIS | Japanese |
264+
| 936 | GBK | Chinese Simplified |
265+
| 949 | EUC-KR | Korean |
266+
| 950 | Big5 | Chinese Traditional |
267+
| 437 | CP437 | OEM United States (DOS) |
268+
269+
#### Examples
270+
271+
**Run a script saved in Windows-1252 encoding:**
272+
```bash
273+
sqlcmd -S myserver -i legacy_script.sql -f 1252
274+
```
275+
276+
**Read UTF-16 input file and write UTF-8 output:**
277+
```bash
278+
sqlcmd -S myserver -i unicode_script.sql -o results.txt -f i:1200,o:65001
279+
```
280+
281+
**Process a Japanese Shift-JIS encoded script:**
282+
```bash
283+
sqlcmd -S myserver -i japanese_data.sql -f 932
284+
```
285+
286+
**Write output in Windows-1252 for legacy applications:**
287+
```bash
288+
sqlcmd -S myserver -Q "SELECT * FROM Products" -o report.txt -f o:1252
289+
```
290+
291+
**List all supported code pages:**
292+
```bash
293+
sqlcmd --list-codepages
294+
```
295+
296+
#### Notes
297+
298+
- When no `-f` flag is specified, sqlcmd auto-detects UTF-16 BOM (Byte Order Mark) in input files and falls back to UTF-8.
299+
- UTF-8 input files with BOM are handled automatically.
300+
- On Windows, additional codepages installed on the system are also available via the Windows API.
301+
- Use `--list-codepages` to see all supported code pages with their names and descriptions.
302+
241303
### Packages
242304

243305
#### sqlcmd executable

cmd/sqlcmd/sqlcmd.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ type SQLCmdArguments struct {
8383
ChangePasswordAndExit string
8484
TraceFile string
8585
CodePage string
86-
ListCodePages bool
86+
// codePageSettings stores the parsed CodePageSettings after validation.
87+
// This avoids parsing CodePage twice (in Validate and run).
88+
codePageSettings *sqlcmd.CodePageSettings
89+
ListCodePages bool
8790
// Keep Help at the end of the list
8891
Help bool
8992
}
@@ -174,8 +177,10 @@ func (a *SQLCmdArguments) Validate(c *cobra.Command) (err error) {
174177
case a.ServerCertificate != "" && !encryptConnectionAllowsTLS(a.EncryptConnection):
175178
err = localizer.Errorf("The -J parameter requires encryption to be enabled (-N true, -N mandatory, or -N strict).")
176179
case a.CodePage != "":
177-
if _, parseErr := sqlcmd.ParseCodePage(a.CodePage); parseErr != nil {
180+
if codePageSettings, parseErr := sqlcmd.ParseCodePage(a.CodePage); parseErr != nil {
178181
err = localizer.Errorf(`'-f %s': %v`, a.CodePage, parseErr)
182+
} else {
183+
a.codePageSettings = codePageSettings
179184
}
180185
}
181186
}
@@ -832,13 +837,9 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
832837
defer s.StopCloseHandler()
833838
s.UnicodeOutputFile = args.UnicodeOutputFile
834839

835-
// Parse and apply codepage settings
836-
if args.CodePage != "" {
837-
codePageSettings, err := sqlcmd.ParseCodePage(args.CodePage)
838-
if err != nil {
839-
return 1, localizer.Errorf("Invalid code page: %v", err)
840-
}
841-
s.CodePage = codePageSettings
840+
// Apply codepage settings (already parsed and validated in Validate)
841+
if args.codePageSettings != nil {
842+
s.CodePage = args.codePageSettings
842843
}
843844

844845
if args.DisableCmd != nil {

pkg/sqlcmd/codepage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ type codepageEntry struct {
3232
var codepageRegistry = map[int]codepageEntry{
3333
// Unicode
3434
65001: {nil, "UTF-8", "Unicode (UTF-8)"},
35-
1200: {unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), "UTF-16LE", "Unicode (UTF-16 Little-Endian)"},
36-
1201: {unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM), "UTF-16BE", "Unicode (UTF-16 Big-Endian)"},
35+
1200: {unicode.UTF16(unicode.LittleEndian, unicode.UseBOM), "UTF-16LE", "Unicode (UTF-16 Little-Endian)"},
36+
1201: {unicode.UTF16(unicode.BigEndian, unicode.UseBOM), "UTF-16BE", "Unicode (UTF-16 Big-Endian)"},
3737

3838
// OEM/DOS codepages
3939
437: {charmap.CodePage437, "CP437", "OEM United States"},

0 commit comments

Comments
 (0)