├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── golangci-lint.yml
│ └── pr-validation.yml
├── .gitignore
├── .pipelines
├── TestSql2017.yml
├── include-install-go-tools.yml
└── include-runtests-linux.yml
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── NOTICE.md
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── build
├── NOTICE.header
├── NOTICE.tpl
├── arch.txt
├── build.cmd
└── build.sh
├── cmd
├── modern
│ ├── doc.go
│ ├── main.go
│ ├── main_test.go
│ ├── options.go
│ ├── root.go
│ ├── root
│ │ ├── config.go
│ │ ├── config
│ │ │ ├── add-context.go
│ │ │ ├── add-context_test.go
│ │ │ ├── add-endpoint.go
│ │ │ ├── add-endpoint_test.go
│ │ │ ├── add-user.go
│ │ │ ├── add-user_test.go
│ │ │ ├── connection-strings.go
│ │ │ ├── connection-strings_test.go
│ │ │ ├── current-context.go
│ │ │ ├── current-context_test.go
│ │ │ ├── delete-context.go
│ │ │ ├── delete-context_test.go
│ │ │ ├── delete-endpoint.go
│ │ │ ├── delete-endpoint_test.go
│ │ │ ├── delete-user.go
│ │ │ ├── delete-user_test.go
│ │ │ ├── get-contexts.go
│ │ │ ├── get-contexts_test.go
│ │ │ ├── get-endpoints.go
│ │ │ ├── get-endpoints_test.go
│ │ │ ├── get-users.go
│ │ │ ├── get-users_test.go
│ │ │ ├── use-context.go
│ │ │ ├── use-context_test.go
│ │ │ ├── view.go
│ │ │ └── view_test.go
│ │ ├── config_test.go
│ │ ├── install.go
│ │ ├── install
│ │ │ ├── edge.go
│ │ │ ├── edge
│ │ │ │ ├── get-tags.go
│ │ │ │ └── get-tags_test.go
│ │ │ ├── edge_test.go
│ │ │ ├── mssql-base.go
│ │ │ ├── mssql-base_test.go
│ │ │ ├── mssql.go
│ │ │ ├── mssql
│ │ │ │ ├── get-tags.go
│ │ │ │ └── get-tags_test.go
│ │ │ └── mssql_test.go
│ │ ├── install_test.go
│ │ ├── open.go
│ │ ├── open
│ │ │ ├── ads.go
│ │ │ ├── ads_darwin.go
│ │ │ ├── ads_linux.go
│ │ │ ├── ads_test.go
│ │ │ ├── ads_windows.go
│ │ │ └── ads_windows_test.go
│ │ ├── open_test.go
│ │ ├── query.go
│ │ ├── query_test.go
│ │ ├── start.go
│ │ ├── start_test.go
│ │ ├── stop.go
│ │ ├── stop_test.go
│ │ ├── uninstall.go
│ │ └── uninstall_test.go
│ ├── root_test.go
│ ├── sqlconfig
│ │ ├── doc.go
│ │ └── sqlconfig.go
│ └── winres
│ │ └── winres.json
├── sqlcmd-linter
│ └── main.go
└── sqlcmd
│ ├── pipe_detection_test.go
│ ├── sqlcmd.go
│ ├── sqlcmd_test.go
│ ├── stdin_console_test.go
│ └── testdata
│ ├── bad.sql
│ ├── create100db.sql
│ ├── drop100db.txt
│ ├── select,100.sql
│ ├── select100.sql
│ ├── selectunicode_BE.txt
│ ├── selectunicode_LE.txt
│ ├── selectutf8.txt
│ ├── selectutf8_bom.txt
│ ├── unicodeout.txt
│ ├── unicodeout_linux.txt
│ ├── utf8out.txt
│ └── utf8out_linux.txt
├── go.mod
├── go.sum
├── internal
├── buffer
│ ├── memory-buffer.go
│ └── memory-buffer_test.go
├── cmdparser
│ ├── cmd.go
│ ├── cmd_test.go
│ ├── dependency
│ │ └── options.go
│ ├── factory.go
│ ├── factory_test.go
│ ├── interface.go
│ ├── options.go
│ ├── test.go
│ ├── test_test.go
│ └── type.go
├── color
│ ├── color.go
│ └── color_test.go
├── config
│ ├── config.go
│ ├── config_test.go
│ ├── context.go
│ ├── endpoint-container.go
│ ├── endpoint-container_test.go
│ ├── endpoint.go
│ ├── endpoint_test.go
│ ├── error.go
│ ├── error_test.go
│ ├── initialize.go
│ ├── trace.go
│ ├── user.go
│ ├── user_test.go
│ ├── viper.go
│ └── viper_test.go
├── container
│ ├── controller.go
│ ├── controller_test.go
│ ├── docker.go
│ ├── error.go
│ ├── error_test.go
│ ├── initialize.go
│ └── trace.go
├── credman
│ ├── credman_linux.go
│ ├── credman_windows.go
│ ├── credman_windows_test.go
│ └── types_windows.go
├── doc.go
├── http
│ ├── error.go
│ ├── error_test.go
│ ├── http.go
│ ├── http_test.go
│ ├── initialize.go
│ └── trace.go
├── intialize.go
├── intialize_test.go
├── io
│ ├── file
│ │ ├── error.go
│ │ ├── error_test.go
│ │ ├── file.go
│ │ ├── file_test.go
│ │ ├── initialize.go
│ │ └── trace.go
│ └── folder
│ │ ├── error.go
│ │ ├── error_test.go
│ │ ├── folder.go
│ │ ├── folder_test.go
│ │ ├── initialize.go
│ │ └── trace.go
├── localizer
│ ├── constants.go
│ ├── localizer.go
│ └── localizer_test.go
├── net
│ ├── error.go
│ ├── error_test.go
│ ├── initialize.go
│ ├── net.go
│ ├── net_test.go
│ └── trace.go
├── output
│ ├── factory.go
│ ├── factory_test.go
│ ├── formatter
│ │ ├── base.go
│ │ ├── base_test.go
│ │ ├── factory.go
│ │ ├── factory_test.go
│ │ ├── interface.go
│ │ ├── json.go
│ │ ├── options.go
│ │ ├── xml.go
│ │ └── yaml.go
│ ├── options.go
│ ├── output.go
│ ├── output_test.go
│ ├── type.go
│ └── verbosity
│ │ └── level.go
├── pal
│ ├── error.go
│ ├── intialize.go
│ ├── pal.go
│ ├── pal_darwin.go
│ ├── pal_linux.go
│ ├── pal_test.go
│ └── pal_windows.go
├── secret
│ ├── encryption_darwin.go
│ ├── encryption_linux.go
│ ├── encryption_windows.go
│ ├── error.go
│ ├── error_test.go
│ ├── generate.go
│ ├── generate_test.go
│ ├── initialize.go
│ ├── secret.go
│ └── secret_test.go
├── sql
│ ├── error.go
│ ├── error_test.go
│ ├── factory.go
│ ├── initialize.go
│ ├── interface.go
│ ├── mock.go
│ ├── mock_test.go
│ ├── mssql.go
│ ├── mssql_test.go
│ ├── trace.go
│ └── types.go
├── test
│ ├── executor.go
│ └── executor_test.go
├── tools
│ ├── factory.go
│ ├── factory_test.go
│ ├── tool
│ │ ├── ads.go
│ │ ├── ads_darwin.go
│ │ ├── ads_linux.go
│ │ ├── ads_test.go
│ │ ├── ads_windows.go
│ │ ├── interface.go
│ │ ├── tool.go
│ │ ├── tool_darwin.go
│ │ ├── tool_linux.go
│ │ ├── tool_test.go
│ │ ├── tool_windows.go
│ │ └── types.go
│ └── tools.go
└── translations
│ ├── LCL
│ ├── de-DE
│ │ └── out.gotext.json.lcl
│ ├── es-ES
│ │ └── out.gotext.json.lcl
│ ├── fr-FR
│ │ └── out.gotext.json.lcl
│ ├── it-IT
│ │ └── out.gotext.json.lcl
│ ├── ja-JP
│ │ └── out.gotext.json.lcl
│ ├── ko-KR
│ │ └── out.gotext.json.lcl
│ ├── pt-BR
│ │ └── out.gotext.json.lcl
│ ├── ru-RU
│ │ └── out.gotext.json.lcl
│ ├── zh-CN
│ │ └── out.gotext.json.lcl
│ └── zh-TW
│ │ └── out.gotext.json.lcl
│ ├── LocProject.json
│ ├── P306PairNamesToProcess.lss
│ ├── catalog.go
│ ├── catalog_test.go
│ ├── locales
│ ├── de-DE
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── en-US
│ │ └── out.gotext.json
│ ├── es-ES
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── fr-FR
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── it-IT
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── ja-JP
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── ko-KR
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── pt-BR
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── ru-RU
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── zh-CN
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ └── zh-TW
│ │ ├── messages.gotext.json
│ │ └── out.gotext.json
│ ├── localized
│ ├── de-DE
│ │ └── out.gotext.json
│ ├── es-ES
│ │ └── out.gotext.json
│ ├── fr-FR
│ │ └── out.gotext.json
│ ├── it-IT
│ │ └── out.gotext.json
│ ├── ja-JP
│ │ └── out.gotext.json
│ ├── ko-KR
│ │ └── out.gotext.json
│ ├── pt-BR
│ │ └── out.gotext.json
│ ├── ru-RU
│ │ └── out.gotext.json
│ ├── zh-CN
│ │ └── out.gotext.json
│ └── zh-TW
│ │ └── out.gotext.json
│ └── translations.go
├── pkg
├── console
│ ├── complete.go
│ ├── complete_test.go
│ ├── console.go
│ ├── console_redirect.go
│ └── console_redirect_test.go
├── sqlcmd-linter
│ ├── imports.go
│ ├── imports_test.go
│ ├── testdata
│ │ └── src
│ │ │ ├── github.com
│ │ │ ├── README.md
│ │ │ ├── alecthomas
│ │ │ │ └── chroma
│ │ │ │ │ └── chroma.go
│ │ │ ├── microsoft
│ │ │ │ └── go-sqlcmd
│ │ │ │ │ └── pkg
│ │ │ │ │ └── sqlcmd
│ │ │ │ │ └── sqlcmd.go
│ │ │ └── stretchr
│ │ │ │ └── testify
│ │ │ │ └── assert
│ │ │ │ └── main.go
│ │ │ ├── imports_linter_tests
│ │ │ ├── cmd
│ │ │ │ └── main
│ │ │ │ │ └── main.go
│ │ │ └── internal
│ │ │ │ └── main.go
│ │ │ └── useassert_linter_tests
│ │ │ └── assert.go
│ ├── useasserts.go
│ └── useasserts_test.go
└── sqlcmd
│ ├── azure_auth.go
│ ├── batch.go
│ ├── batch_test.go
│ ├── commands.go
│ ├── commands_test.go
│ ├── connect.go
│ ├── errors.go
│ ├── exec_darwin.go
│ ├── exec_linux.go
│ ├── exec_windows.go
│ ├── format.go
│ ├── format_darwin.go
│ ├── format_linux.go
│ ├── format_test.go
│ ├── format_windows.go
│ ├── parse.go
│ ├── parse_test.go
│ ├── sqlcmd.go
│ ├── sqlcmd_test.go
│ ├── sqlcmd_windows_amd64.go
│ ├── testdata
│ ├── blanks.sql
│ ├── quotedidentifiers.sql
│ ├── selectdates.sql
│ ├── singlebatchnogo.sql
│ ├── testerrorredirection.sql
│ ├── twobatchnoendinggo.sql
│ ├── twobatchwithgo.sql
│ └── variablesnogo.sql
│ ├── util.go
│ ├── variables.go
│ └── variables_test.go
├── release
├── linux
│ ├── deb
│ │ ├── README.md
│ │ ├── build-pkg.sh
│ │ ├── pipeline-test.sh
│ │ ├── pipeline.sh
│ │ └── prepare-rules.sh
│ ├── docker
│ │ ├── README.md
│ │ ├── pipeline-test.sh
│ │ └── pipeline.sh
│ └── rpm
│ │ ├── README.md
│ │ ├── build-rpm.sh
│ │ ├── pipeline-test.sh
│ │ ├── pipeline.sh
│ │ └── sqlcmd.spec
└── windows
│ ├── choco
│ ├── sqlcmd.nuspec
│ └── tools
│ │ ├── LICENSE.txt
│ │ ├── VERIFICATION.txt
│ │ └── chocolateyinstall.ps1
│ └── msi
│ ├── README.md
│ ├── eula
│ └── eulatext_1033.rtf
│ ├── product.wxs
│ ├── resources
│ ├── CLI_LICENSE.rtf
│ ├── banner.bmp
│ ├── dialog.bmp
│ ├── go-sqlcmd_256.png
│ └── sqlcmd.ico
│ ├── scripts
│ ├── pipeline-test.ps1
│ └── pipeline.cmd
│ ├── sqlcmd.sln
│ └── sqlcmd.wixproj
└── testdata
└── sql.txt
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*.go]
5 | indent_style = tab
6 | # (Please don't specify an indent_size here; that has too many unintended consequences.)
7 |
8 | # IDE0073: File header
9 | file_header_template = Copyright (c) Microsoft Corporation.\nLicensed under the MIT license.
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 |
--------------------------------------------------------------------------------
/.github/workflows/golangci-lint.yml:
--------------------------------------------------------------------------------
1 | name: golangci-lint
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | jobs:
8 | golangci-pr:
9 | name: lint-pr-changes
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: stable
15 | - uses: actions/checkout@v4
16 | - name: golangci-lint
17 | uses: golangci/golangci-lint-action@v6
18 | with:
19 | version: latest
20 | only-new-issues: true
21 |
--------------------------------------------------------------------------------
/.github/workflows/pr-validation.yml:
--------------------------------------------------------------------------------
1 | name: pr-validation
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - name: Setup go
14 | uses: actions/setup-go@v2
15 | with:
16 | go-version: '1.22'
17 | - name: Run tests against Linux SQL
18 | run: |
19 | go version
20 | cd cmd/sqlcmd
21 | go get -d
22 | go build .
23 | export SQLCMDPASSWORD=$(date +%s|sha256sum|base64|head -c 32)
24 | export SQLCMDUSER=sa
25 | docker run -m 2GB -e ACCEPT_EULA=1 -d --name sql2022 -p:1433:1433 -e SA_PASSWORD=$SQLCMDPASSWORD mcr.microsoft.com/mssql/server:2022-latest
26 | cd ../..
27 | go test -v ./...
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 | bin
8 | obj
9 |
10 | # Test binary, built with `go test -c`
11 | *.test
12 |
13 | # Output of the go coverage tool, specifically when used with LiteIDE
14 | *.out
15 |
16 | # Dependency directories (remove the comment below to include it)
17 | # vendor/
18 |
19 | coverage.json
20 | coverage.txt
21 | coverage.xml
22 | testresults.xml
23 |
24 | # .syso is generated by go-winres. Only needed for official builds
25 | *.syso
26 |
27 | # IDE files
28 | .idea
29 |
30 | darwin-amd64/sqlcmd
31 | darwin-arm64/sqlcmd
32 | linux-amd64/sqlcmd
33 | linux-arm64/sqlcmd
34 | linux-s390x/sqlcmd
35 |
--------------------------------------------------------------------------------
/.pipelines/include-install-go-tools.yml:
--------------------------------------------------------------------------------
1 | steps:
2 | - task: GoTool@0
3 | inputs:
4 | version: '1.22.10'
5 | - task: Go@0
6 | displayName: 'Go: get dependencies'
7 | inputs:
8 | command: 'get'
9 | arguments: '-d'
10 | workingDirectory: '$(Build.SourcesDirectory)/cmd/sqlcmd'
11 |
12 | - task: Go@0
13 | displayName: 'Go: install gotest.tools/gotestsum'
14 | inputs:
15 | command: 'custom'
16 | customCommand: 'install'
17 | arguments: 'gotest.tools/gotestsum@latest'
18 | workingDirectory: '$(System.DefaultWorkingDirectory)'
19 |
20 | - task: Go@0
21 | displayName: 'Go: install github.com/axw/gocov/gocov'
22 | inputs:
23 | command: 'custom'
24 | customCommand: 'install'
25 | arguments: 'github.com/axw/gocov/gocov@latest'
26 | workingDirectory: '$(System.DefaultWorkingDirectory)'
27 |
28 | - task: Go@0
29 | displayName: 'Go: install github.com/axw/gocov/gocov'
30 | inputs:
31 | command: 'custom'
32 | customCommand: 'install'
33 | arguments: 'github.com/AlekSi/gocov-xml@latest'
34 | workingDirectory: '$(System.DefaultWorkingDirectory)'
35 |
--------------------------------------------------------------------------------
/.pipelines/include-runtests-linux.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | - name: RunName
3 | type: string
4 | - name: SQLCMDUSER
5 | type: string
6 | default: ''
7 | - name: SQLPASSWORD
8 | type: string
9 | default: ''
10 | - name: AZURECLIENTSECRET
11 | type: string
12 | default: ''
13 | - name: SQLCMDSERVER
14 | type: string
15 | default: .
16 | - name: SQLCMDDBNAME
17 | type: string
18 | default: ''
19 | steps:
20 | - script: |
21 | ~/go/bin/gotestsum --junitfile "${{ parameters.RunName }}.testresults.xml" -- ./... -coverprofile="${{ parameters.RunName }}.coverage.txt" -covermode count
22 | ~/go/bin/gocov convert "${{ parameters.RunName }}.coverage.txt" > "${{ parameters.RunName }}.coverage.json"
23 | ~/go/bin/gocov-xml < "${{ parameters.RunName }}.coverage.json" > ${{ parameters.RunName }}.coverage.xml
24 | mkdir -p coverage
25 | workingDirectory: '$(Build.SourcesDirectory)'
26 | displayName: 'run tests'
27 | env:
28 | SQLPASSWORD: ${{ parameters.SQLPASSWORD }}
29 | SQLCMDUSER: ${{ parameters.SQLCMDUSER }}
30 | SQLCMDPASSWORD: ${{ parameters.SQLPASSWORD }}
31 | AZURE_TENANT_ID: $(AZURE_TENANT_ID)
32 | AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
33 | AZURE_CLIENT_SECRET: ${{ parameters.AZURECLIENTSECRET }}
34 | SQLCMDSERVER: ${{ parameters.SQLCMDSERVER }}
35 | SQLCMDDBNAME: ${{ parameters.SQLCMDDBNAME }}
36 | continueOnError: true
37 |
38 | - task: PublishTestResults@2
39 | displayName: "Publish junit-style results"
40 | inputs:
41 | testResultsFiles: '${{ parameters.RunName }}.testresults.xml'
42 | testResultsFormat: JUnit
43 | searchFolder: '$(Build.SourcesDirectory)'
44 | testRunTitle: '${{ parameters.RunName }} - $(Build.SourceBranchName)'
45 | failTaskOnFailedTests: true
46 | condition: always()
47 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 |
10 | "name": "Attach to Process",
11 |
12 | "type": "go",
13 |
14 | "request": "attach",
15 |
16 | "mode": "local",
17 |
18 | "processId": 0
19 |
20 | },
21 | {
22 | "name" : "Run query and exit",
23 | "type" : "go",
24 | "request": "launch",
25 | "mode" : "auto",
26 | "program": "${workspaceFolder}/cmd/modern",
27 | "args" : ["-S", "davidshi-2022", "-g", "-Q", "select nvarcharcol from encrypt.dbo.encrypted", "--driver-logging-level=65"],
28 | },
29 | {
30 | "name" : "Run file query",
31 | "type" : "go",
32 | "request": "launch",
33 | "mode" : "auto",
34 | "program": "${workspaceFolder}/cmd/modern",
35 | "args" : ["-S", "np:.", "-i", "${workspaceFolder}/cmd/sqlcmd/testdata/select100.sql"],
36 | },
37 | {
38 | "name" : "Run sqlcmdlinter",
39 | "type" : "go",
40 | "request" : "launch",
41 | "mode" : "auto",
42 | "program": "${workspaceFolder}/cmd/sqlcmd-linter",
43 | "args" : ["${workspaceFolder}/..."]
44 |
45 | }
46 | ]
47 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "go.lintTool": "golangci-lint",
3 | "go.lintOnSave": "workspace",
4 | "azure-pipelines.1ESPipelineTemplatesSchemaFile": true
5 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | ]
7 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------
2 | # Copyright (c) Microsoft Corporation.
3 | # Licensed under the MIT license.
4 | #------------------------------------------------------------------------------
5 |
6 | # Example:
7 | # docker run --rm microsoft/sqlcmd sqlcmd --help
8 | #
9 |
10 | FROM scratch
11 | ARG BUILD_DATE
12 | ARG PACKAGE_VERSION
13 |
14 | LABEL maintainer="Microsoft" \
15 | org.label-schema.schema-version="1.0" \
16 | org.label-schema.vendor="Microsoft" \
17 | org.label-schema.name="SQLCMD CLI" \
18 | org.label-schema.version=$PACKAGE_VERSION \
19 | org.label-schema.license="https://github.com/microsoft/go-sqlcmd/blob/main/LICENSE" \
20 | org.label-schema.description="The MSSQL SQLCMD CLI tool" \
21 | org.label-schema.url="https://github.com/microsoft/go-sqlcmd" \
22 | org.label-schema.usage="https://docs.microsoft.com/sql/tools/sqlcmd-utility" \
23 | org.label-schema.build-date=$BUILD_DATE \
24 | org.label-schema.docker.cmd="docker run -it microsoft/sqlcmd:$PACKAGE_VERSION"
25 |
26 | COPY ./sqlcmd /usr/bin/sqlcmd
27 |
28 | CMD ["sqlcmd"]
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License (MIT)
2 |
3 | Copyright © Microsoft Corp.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # TODO: The maintainer of this repo has not yet edited this file
2 |
3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
4 |
5 | - **No CSS support:** Fill out this template with information about how to file issues and get help.
6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport).
7 | - **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide.
8 |
9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
10 |
11 | # Support
12 |
13 | ## How to file issues and get help
14 |
15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing
16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or
17 | feature request as a new Issue.
18 |
19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE
20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER
21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
22 |
23 | ## Microsoft Support Policy
24 |
25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
26 |
--------------------------------------------------------------------------------
/build/NOTICE.header:
--------------------------------------------------------------------------------
1 | # NOTICES
2 |
3 | This repository incorporates material as listed below or described in the code.
4 |
--------------------------------------------------------------------------------
/build/NOTICE.tpl:
--------------------------------------------------------------------------------
1 |
2 | {{ range . }}
3 | ## {{ .Name }}
4 |
5 | * Name: {{ .Name }}
6 | * Version: {{ .Version }}
7 | * License: [{{ .LicenseName }}]({{ .LicenseURL }})
8 |
9 | ```
10 | {{ .LicenseText }}
11 | ```
12 | {{ end }}
13 |
--------------------------------------------------------------------------------
/build/arch.txt:
--------------------------------------------------------------------------------
1 | darwin,amd64,sqlcmd
2 | darwin,arm64,sqlcmd
3 | linux,amd64,sqlcmd
4 | linux,arm64,sqlcmd
5 | linux,s390x,sqlcmd
6 | windows,arm64,sqlcmd.exe
7 | windows,amd64,sqlcmd.exe
8 | windows,arm,sqlcmd.exe
--------------------------------------------------------------------------------
/build/build.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | REM We get the value of the escape character by using PROMPT $E
4 | for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
5 | set "DEL=%%a"
6 | set "ESC=%%b"
7 | )
8 |
9 | REM Get Version Tag
10 | for /f %%i in ('"git describe --tags --abbrev=0"') do set sqlcmdVersion=%%i
11 |
12 | setlocal
13 | SET RED=%ESC%[1;31m
14 | @echo %RED%
15 | REM run the custom sqlcmd linter for code style enforcement
16 | REM using for/do instead of running it directly so the status code isn't checked by the shell.
17 | REM Once we are prepared to block the build with the linter we will move this step into a pipeline
18 | @for /F "usebackq" %%l in (`go run cmd\sqlcmd-linter\main.go -test %~dp0../...`) DO echo %%l
19 | @echo %ESC%[0m
20 |
21 | if not exist %gopath%\bin\go-winres.exe (
22 | go install github.com/tc-hib/go-winres@latest
23 | )
24 | if not exist %gopath%\bin\gotext.exe (
25 | go install golang.org/x/text/cmd/gotext@latest
26 | )
27 |
28 | REM go-winres likes to append instead of overwrite so delete existing resource file
29 | del %~dp0..\cmd\modern\*.syso
30 |
31 | REM generates translations file and resources
32 | go generate %~dp0../... 2> %~dp0generate.txt
33 | if NOT ERRORLEVEL 0 (
34 | type %~dp0generate.txt
35 | goto :end
36 | ) else (
37 | echo Fix any conflicting localizable strings:
38 | @echo %RED%
39 | @findstr conflicting "%~dp0generate.txt"
40 | @echo %ESC%[0m
41 | )
42 | endlocal
43 |
44 | REM Generates sqlcmd.exe in the root dir of the repo
45 | go build -o %~dp0..\sqlcmd.exe -ldflags="-X main.version=%sqlcmdVersion%" %~dp0..\cmd\modern
46 |
47 | REM Generate NOTICE
48 | if not exist %gopath%\bin\go-licenses.exe (
49 | go install github.com/google/go-licenses@latest
50 | )
51 | go-licenses report github.com/microsoft/go-sqlcmd/cmd/modern --template build\NOTICE.tpl --ignore github.com/microsoft > %~dp0notice.txt 2>nul
52 | copy %~dp0NOTICE.header + %~dp0notice.txt %~dp0..\NOTICE.md
53 | del %~dp0notice.txt
54 |
55 | REM Generates all versions of sqlcmd in platform-specific folder
56 | setlocal
57 |
58 | for /F "tokens=1-3 delims=," %%i in (%~dp0arch.txt) do set GOOS=%%i&set GOARCH=%%j&go build -o %~dp0..\%%i-%%j\%%k -ldflags="-X main.version=%sqlcmdVersion%" %~dp0..\cmd\modern
59 | endlocal
60 |
61 | :end
62 | del %~dp0generate.txt
63 |
--------------------------------------------------------------------------------
/build/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | scriptdir=`dirname $0`
3 | versionTag=`git describe --tags --abbrev=0`
4 | go build -o $scriptdir/../sqlcmd -ldflags="-X main.version=$versionTag" $scriptdir/../cmd/modern
5 |
6 | go install github.com/google/go-licenses@latest
7 | go-licenses report github.com/microsoft/go-sqlcmd/cmd/modern --template build/NOTICE.tpl --ignore github.com/microsoft > $scriptDir/notice.txt
8 | cat $scriptDir/NOTICE.header $scriptDir/notice.txt > $scriptDir/../NOTICE.md
9 | rm $scriptDir/notice.txt
10 |
--------------------------------------------------------------------------------
/cmd/modern/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | /*
7 | Main package (main.go) is the entry point for the sqlcmd CLI application.
8 |
9 | To follow the flow of this code:
10 |
11 | 1. enter through main.go, (TEMPORARY: decision made whether to invoke the modern
12 | cobra CLI
13 | 2. Then cmd/cmd.go, see the init() func `New` the `Root` cmd (and all its
14 | subcommands)
15 | 3. The command-line is then parsed and internal.Initialize() runs (with
16 | the logging level, config file path, error handling and trace support passed
17 | into internal packages)
18 | 4. Now go to the cmd/root/… folder structure, and read the DefineCommand
19 | function for the command (sqlcmd install, sqlcmd query etc.) being run
20 | 5. Each cmd/root/... command has a `run` method that performs the action
21 | 6. All the commands (cmd/root/…) use the /internal packages to abstract from error
22 | handling and trace (non-localized) logging (as can be seen from the `import`
23 | for each command (in /cmd/root/...)).
24 |
25 | This code follows the Go Style Guide
26 |
27 | - https://google.github.io/styleguide/go/
28 | - https://go.dev/doc/effective_go
29 | - https://github.com/golang-standards/project-layout
30 |
31 | Exceptions to Go Style Guide:
32 |
33 | - None
34 | */
35 |
--------------------------------------------------------------------------------
/cmd/modern/main_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | import (
7 | "errors"
8 | "github.com/microsoft/go-sqlcmd/internal/buffer"
9 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
10 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
11 | "github.com/microsoft/go-sqlcmd/internal/output"
12 | "github.com/microsoft/go-sqlcmd/internal/pal"
13 | "github.com/stretchr/testify/assert"
14 | "os"
15 | "testing"
16 | )
17 |
18 | func TestMainStart(t *testing.T) {
19 | os.Args[1] = "--help"
20 | main()
21 | }
22 |
23 | func TestInitializeCallback(t *testing.T) {
24 | rootCmd = cmdparser.New[*Root](dependency.Options{})
25 | initializeCallback()
26 | }
27 |
28 | func TestDisplayHints(t *testing.T) {
29 | buf := buffer.NewMemoryBuffer()
30 | outputter = output.New(output.Options{StandardWriter: buf})
31 | displayHints([]string{"This is a hint"})
32 | assert.Equal(t, pal.LineBreak()+
33 | "HINT:"+
34 | pal.LineBreak()+
35 | " 1. This is a hint"+pal.LineBreak()+pal.LineBreak(), buf.String())
36 | err := buf.Close()
37 | checkErr(err)
38 | }
39 |
40 | func TestCheckErr(t *testing.T) {
41 | rootCmd = cmdparser.New[*Root](dependency.Options{})
42 | rootCmd.loggingLevel = 4
43 | checkErr(nil)
44 | assert.Panics(t, func() {
45 | checkErr(errors.New("test error"))
46 | })
47 | }
48 |
--------------------------------------------------------------------------------
/cmd/modern/options.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | type GlobalOptions struct {
7 | TrustServerCertificate bool
8 | DatabaseName string
9 | UseTrustedConnection bool
10 | UserName string
11 | Endpoint string
12 | AuthenticationMethod string
13 | UseAad bool
14 | PacketSize int
15 | LoginTimeout int
16 | WorkstationName string
17 | ApplicationIntent string
18 | Encrypt string
19 | DriverLogLevel int
20 | }
21 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/add-context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestAddContext(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddEndpoint]()
15 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint")
16 | }
17 |
18 | func TestNegAddContext(t *testing.T) {
19 | cmdparser.TestSetup(t)
20 | assert.Panics(t, func() {
21 | cmdparser.TestCmd[*AddContext]("--endpoint does-not-exist")
22 | })
23 | }
24 |
25 | func TestNegAddContext2(t *testing.T) {
26 | cmdparser.TestSetup(t)
27 | cmdparser.TestCmd[*AddEndpoint]()
28 | assert.Panics(t, func() {
29 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint --user does-not-exist")
30 | })
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/add-endpoint_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | func TestAddEndpoint(t *testing.T) {
12 | cmdparser.TestSetup(t)
13 | cmdparser.TestCmd[*AddEndpoint]()
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/connection-strings_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/output"
10 | "github.com/stretchr/testify/assert"
11 | "os"
12 | "testing"
13 | )
14 |
15 | // TestConnectionStrings tests that the `sqlcmd config connection-strings` command
16 | // works as expected
17 | func TestConnectionStrings(t *testing.T) {
18 | cmdparser.TestSetup(t)
19 |
20 | output := output.New(output.Options{HintHandler: func(hints []string) {}, ErrorHandler: func(err error) {}})
21 | options := internal.InitializeOptions{
22 | ErrorHandler: func(err error) {
23 | if err != nil {
24 | panic(err)
25 | }
26 | },
27 | HintHandler: func(strings []string) {},
28 | TraceHandler: output.Tracef,
29 | LineBreak: "\n",
30 | }
31 | internal.Initialize(options)
32 |
33 | if os.Getenv("SQLCMDPASSWORD") == "" &&
34 | os.Getenv("SQLCMD_PASSWORD") == "" {
35 | os.Setenv("SQLCMDPASSWORD", "it's-a-secret")
36 | defer os.Setenv("SQLCMDPASSWORD", "")
37 | }
38 |
39 | cmdparser.TestCmd[*AddEndpoint]()
40 | cmdparser.TestCmd[*AddUser]("--username user --password-encryption none")
41 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint --user user")
42 | cmdparser.TestCmd[*ConnectionStrings]()
43 |
44 | // Add endpoint with no user
45 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint")
46 | cmdparser.TestCmd[*ConnectionStrings]()
47 |
48 | // Add endpoint to Azure SQL (connection strings won't Trust server cert)
49 | cmdparser.TestCmd[*AddEndpoint]("--address server.database.windows.net")
50 | cmdparser.TestCmd[*AddUser]("--username user --password-encryption none")
51 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint2 --user user")
52 |
53 | result := cmdparser.TestCmd[*ConnectionStrings]()
54 | assert.Contains(t, result, "database=master")
55 |
56 | result = cmdparser.TestCmd[*ConnectionStrings]("--database tempdb")
57 | assert.NotContains(t, result, "database=master")
58 | assert.Contains(t, result, "database=tempdb")
59 | }
60 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/current-context.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // CurrentContext implements the `sqlcmd config current-context` command
13 | type CurrentContext struct {
14 | cmdparser.Cmd
15 | }
16 |
17 | func (c *CurrentContext) DefineCommand(...cmdparser.CommandOptions) {
18 | options := cmdparser.CommandOptions{
19 | Use: "current-context",
20 | Short: localizer.Sprintf("Display the current-context"),
21 | Examples: []cmdparser.ExampleOptions{
22 | {
23 | Description: localizer.Sprintf("Display the current-context"),
24 | Steps: []string{
25 | "sqlcmd config current-context"},
26 | },
27 | },
28 | Run: c.run}
29 |
30 | c.Cmd.DefineCommand(options)
31 | }
32 |
33 | func (c *CurrentContext) run() {
34 | output := c.Output()
35 | output.Infof("%v\n", config.CurrentContextName())
36 | }
37 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/current-context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | func TestCurrentContext(t *testing.T) {
12 | cmdparser.TestSetup(t)
13 | cmdparser.TestCmd[*CurrentContext]()
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/delete-context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestDeleteContext(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddUser]("--name delete-test --username user --auth-type other")
15 | cmdparser.TestCmd[*AddEndpoint]()
16 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint --user delete-test")
17 | cmdparser.TestCmd[*DeleteContext]("--name context")
18 | }
19 |
20 | func TestNegDeleteContext(t *testing.T) {
21 | cmdparser.TestSetup(t)
22 | assert.Panics(t, func() {
23 | cmdparser.TestCmd[*DeleteContext]()
24 | })
25 | }
26 |
27 | func TestNegDeleteContext2(t *testing.T) {
28 | cmdparser.TestSetup(t)
29 | assert.Panics(t, func() {
30 | cmdparser.TestCmd[*DeleteContext]("--name does-not-exist")
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/delete-endpoint.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // DeleteEndpoint implements the `sqlcmd config delete-endpoint` command
13 | type DeleteEndpoint struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | }
18 |
19 | func (c *DeleteEndpoint) DefineCommand(...cmdparser.CommandOptions) {
20 | options := cmdparser.CommandOptions{
21 | Use: "delete-endpoint",
22 | Short: localizer.Sprintf("Delete an endpoint"),
23 | Examples: []cmdparser.ExampleOptions{
24 | {
25 | Description: localizer.Sprintf("Delete an endpoint"),
26 | Steps: []string{
27 | "sqlcmd config delete-endpoint --name my-endpoint",
28 | "sqlcmd config delete-context endpoint"},
29 | },
30 | },
31 | Run: c.run,
32 |
33 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{Flag: "name", Value: &c.name},
34 | }
35 |
36 | c.Cmd.DefineCommand(options)
37 |
38 | c.AddFlag(cmdparser.FlagOptions{
39 | String: &c.name,
40 | Name: "name",
41 | Usage: localizer.Sprintf("Name of endpoint to delete")})
42 | }
43 |
44 | // run is used to delete an endpoint with the given name. If the specified endpoint
45 | // does not exist, the function will print an error message and return. If the
46 | // endpoint exists, it will be deleted and a success message will be printed.
47 | func (c *DeleteEndpoint) run() {
48 | output := c.Output()
49 |
50 | if c.name == "" {
51 | output.Fatal(localizer.Sprintf("Endpoint name must be provided. Provide endpoint name with %s flag", localizer.NameFlag))
52 | }
53 |
54 | if config.EndpointExists(c.name) {
55 | config.DeleteEndpoint(c.name)
56 | } else {
57 | output.FatalWithHintExamples([][]string{
58 | {localizer.Sprintf("View endpoints"), "sqlcmd config get-endpoints"},
59 | },
60 | localizer.Sprintf("Endpoint '%v' does not exist", c.name))
61 | }
62 |
63 | output.Info(localizer.Sprintf("Endpoint '%v' deleted", c.name))
64 | }
65 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/delete-endpoint_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestDeleteEndpoint(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddEndpoint]()
15 | cmdparser.TestCmd[*DeleteEndpoint]("--name endpoint")
16 | }
17 |
18 | func TestNegDeleteEndpoint(t *testing.T) {
19 | cmdparser.TestSetup(t)
20 | assert.Panics(t, func() {
21 | cmdparser.TestCmd[*DeleteEndpoint]()
22 | })
23 | }
24 |
25 | func TestNegDeleteEndpoint2(t *testing.T) {
26 | cmdparser.TestSetup(t)
27 | assert.Panics(t, func() {
28 | cmdparser.TestCmd[*DeleteEndpoint]("--name does-not-exist")
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/delete-user.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // DeleteUser implements the `sqlcmd config delete-user` command
13 | type DeleteUser struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | }
18 |
19 | func (c *DeleteUser) DefineCommand(...cmdparser.CommandOptions) {
20 | options := cmdparser.CommandOptions{
21 | Use: "delete-user",
22 | Short: localizer.Sprintf("Delete a user"),
23 | Examples: []cmdparser.ExampleOptions{
24 | {
25 | Description: localizer.Sprintf("Delete a user"),
26 | Steps: []string{
27 | "sqlcmd config delete-user --name user1",
28 | "sqlcmd config delete-user user1"}},
29 | },
30 | Run: c.run,
31 |
32 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{
33 | Flag: "name", Value: &c.name},
34 | }
35 |
36 | c.Cmd.DefineCommand(options)
37 |
38 | c.AddFlag(cmdparser.FlagOptions{
39 | String: &c.name,
40 | Name: "name",
41 | Usage: localizer.Sprintf("Name of user to delete")})
42 | }
43 |
44 | func (c *DeleteUser) run() {
45 | output := c.Output()
46 |
47 | if c.name == "" {
48 | output.Fatal(localizer.Sprintf("User name must be provided. Provide user name with %s flag", localizer.NameFlag))
49 | }
50 |
51 | if config.UserNameExists(c.name) {
52 | config.DeleteUser(c.name)
53 | } else {
54 | output.FatalWithHintExamples([][]string{
55 | {localizer.Sprintf("View users"), "sqlcmd config get-users"},
56 | },
57 | localizer.Sprintf("User %q does not exist", c.name))
58 | }
59 |
60 | output.Info(localizer.Sprintf("User %q deleted", c.name))
61 | }
62 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/delete-user_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "os"
10 | "testing"
11 | )
12 |
13 | func TestDeleteUser(t *testing.T) {
14 | cmdparser.TestSetup(t)
15 |
16 | // SQLCMDPASSWORD is already set in the build pipelines, so don't
17 | // overwrite it here.
18 | if os.Getenv("SQLCMDPASSWORD") == "" && os.Getenv("SQLCMD_PASSWORD") == "" {
19 | os.Setenv("SQLCMDPASSWORD", "it's-a-secret")
20 | defer os.Setenv("SQLCMDPASSWORD", "")
21 | }
22 | cmdparser.TestSetup(t)
23 | cmdparser.TestCmd[*AddUser]("--username user1 --password-encryption none")
24 | cmdparser.TestCmd[*DeleteUser]("--name user")
25 | }
26 |
27 | func TestNegDeleteUserNoName(t *testing.T) {
28 | cmdparser.TestSetup(t)
29 |
30 | assert.Panics(t, func() {
31 | cmdparser.TestCmd[*DeleteUser]()
32 | })
33 | }
34 |
35 | func TestNegDeleteUserInvalidName(t *testing.T) {
36 | cmdparser.TestSetup(t)
37 |
38 | assert.Panics(t, func() {
39 | cmdparser.TestCmd[*DeleteUser]("--name bad-bad")
40 | })
41 | }
42 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-contexts.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // GetContexts implements the `sqlcmd config get-contexts` command
13 | type GetContexts struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | detailed bool
18 | }
19 |
20 | func (c *GetContexts) DefineCommand(...cmdparser.CommandOptions) {
21 | options := cmdparser.CommandOptions{
22 | Use: "get-contexts",
23 | Short: localizer.Sprintf("Display one or many contexts from the sqlconfig file"),
24 | Examples: []cmdparser.ExampleOptions{
25 | {
26 | Description: localizer.Sprintf("List all the context names in your sqlconfig file"),
27 | Steps: []string{"sqlcmd config get-contexts"},
28 | },
29 | {
30 | Description: localizer.Sprintf("List all the contexts in your sqlconfig file"),
31 | Steps: []string{"sqlcmd config get-contexts --detailed"},
32 | },
33 | {
34 | Description: localizer.Sprintf("Describe one context in your sqlconfig file"),
35 | Steps: []string{"sqlcmd config get-contexts my-context"},
36 | },
37 | },
38 | Run: c.run,
39 |
40 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{Flag: "name", Value: &c.name},
41 | }
42 |
43 | c.Cmd.DefineCommand(options)
44 |
45 | c.AddFlag(cmdparser.FlagOptions{
46 | String: &c.name,
47 | Name: "name",
48 | Usage: localizer.Sprintf("Context name to view details of")})
49 |
50 | c.AddFlag(cmdparser.FlagOptions{
51 | Bool: &c.detailed,
52 | Name: "detailed",
53 | Usage: localizer.Sprintf("Include context details")})
54 | }
55 |
56 | func (c *GetContexts) run() {
57 | output := c.Output()
58 |
59 | if c.name != "" {
60 | if config.ContextExists(c.name) {
61 | context := config.GetContext(c.name)
62 | output.Struct(context)
63 | } else {
64 | output.FatalWithHints(
65 | []string{localizer.Sprintf("To view available contexts run `%s`", localizer.GetContextCommand)},
66 | localizer.Sprintf("error: no context exists with the name: \"%v\"", c.name))
67 | }
68 | } else {
69 | config.OutputContexts(output.Struct, c.detailed)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-contexts_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestGetContexts(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddEndpoint]("--name endpoint")
15 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint")
16 | cmdparser.TestCmd[*GetContexts]()
17 | cmdparser.TestCmd[*GetContexts]("context")
18 | }
19 |
20 | func TestNegGetContexts(t *testing.T) {
21 | cmdparser.TestSetup(t)
22 | assert.Panics(t, func() {
23 | cmdparser.TestCmd[*GetContexts]("does-not-exist")
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-endpoints.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // GetEndpoints implements the `sqlcmd config get-endpoints` command
13 | type GetEndpoints struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | detailed bool
18 | }
19 |
20 | func (c *GetEndpoints) DefineCommand(...cmdparser.CommandOptions) {
21 | options := cmdparser.CommandOptions{
22 | Use: "get-endpoints",
23 | Short: localizer.Sprintf("Display one or many endpoints from the sqlconfig file"),
24 | Examples: []cmdparser.ExampleOptions{
25 | {
26 | Description: localizer.Sprintf("List all the endpoints in your sqlconfig file"),
27 | Steps: []string{"sqlcmd config get-endpoints"}},
28 | {
29 | Description: localizer.Sprintf("List all the endpoints in your sqlconfig file"),
30 | Steps: []string{"sqlcmd config get-endpoints --detailed"}},
31 | {
32 | Description: localizer.Sprintf("Describe one endpoint in your sqlconfig file"),
33 | Steps: []string{"sqlcmd config get-endpoints my-endpoint"}},
34 | },
35 | Run: c.run,
36 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{Flag: "name", Value: &c.name},
37 | }
38 |
39 | c.Cmd.DefineCommand(options)
40 |
41 | c.AddFlag(cmdparser.FlagOptions{
42 | String: &c.name,
43 | Name: "name",
44 | Usage: localizer.Sprintf("Endpoint name to view details of")})
45 |
46 | c.AddFlag(cmdparser.FlagOptions{
47 | Bool: &c.detailed,
48 | Name: "detailed",
49 | Usage: localizer.Sprintf("Include endpoint details")})
50 | }
51 |
52 | func (c *GetEndpoints) run() {
53 | output := c.Output()
54 |
55 | if c.name != "" {
56 | if config.EndpointExists(c.name) {
57 | context := config.GetEndpoint(c.name)
58 | output.Struct(context)
59 | } else {
60 | output.FatalWithHints(
61 | []string{localizer.Sprintf("To view available endpoints run `%s`", localizer.GetEndpointsCommand)},
62 | localizer.Sprintf("error: no endpoint exists with the name: \"%v\"", c.name))
63 | }
64 | } else {
65 | config.OutputEndpoints(output.Struct, c.detailed)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-endpoints_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestGetEndpoints(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddEndpoint]("--name endpoint")
15 | cmdparser.TestCmd[*GetEndpoints]()
16 | cmdparser.TestCmd[*GetEndpoints]("endpoint")
17 |
18 | }
19 |
20 | func TestNegGetEndpoints(t *testing.T) {
21 | cmdparser.TestSetup(t)
22 | assert.Panics(t, func() {
23 | cmdparser.TestCmd[*GetEndpoints]("does-not-exist")
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-users.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // GetUsers implements the `sqlcmd config get-users` command
13 | type GetUsers struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | detailed bool
18 | }
19 |
20 | func (c *GetUsers) DefineCommand(...cmdparser.CommandOptions) {
21 | options := cmdparser.CommandOptions{
22 | Use: "get-users",
23 | Short: localizer.Sprintf("Display one or many users from the sqlconfig file"),
24 | Examples: []cmdparser.ExampleOptions{
25 | {
26 | Description: localizer.Sprintf("List all the users in your sqlconfig file"),
27 | Steps: []string{"sqlcmd config get-users"},
28 | },
29 | {
30 | Description: localizer.Sprintf("List all the users in your sqlconfig file"),
31 | Steps: []string{"sqlcmd config get-users --detailed"},
32 | },
33 | {
34 | Description: localizer.Sprintf("Describe one user in your sqlconfig file"),
35 | Steps: []string{"sqlcmd config get-users user1"},
36 | },
37 | },
38 | Run: c.run,
39 |
40 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{Flag: "name", Value: &c.name},
41 | }
42 |
43 | c.Cmd.DefineCommand(options)
44 |
45 | c.AddFlag(cmdparser.FlagOptions{
46 | String: &c.name,
47 | Name: "name",
48 | Usage: localizer.Sprintf("User name to view details of")})
49 |
50 | c.AddFlag(cmdparser.FlagOptions{
51 | Bool: &c.detailed,
52 | Name: "detailed",
53 | Usage: localizer.Sprintf("Include user details")})
54 | }
55 |
56 | func (c *GetUsers) run() {
57 | output := c.Output()
58 |
59 | if c.name != "" {
60 | if config.UserNameExists(c.name) {
61 | user := config.GetUser(c.name)
62 | output.Struct(user)
63 | } else {
64 | output.FatalWithHints(
65 | []string{localizer.Sprintf("To view available users run `%s`", localizer.GetUsersCommand)},
66 | localizer.Sprintf("error: no user exists with the name: \"%v\"", c.name))
67 | }
68 | } else {
69 | config.OutputUsers(output.Struct, c.detailed)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/get-users_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "os"
10 | "testing"
11 | )
12 |
13 | func TestGetUsers(t *testing.T) {
14 | if os.Getenv("SQLCMD_PASSWORD") == "" {
15 | os.Setenv("SQLCMD_PASSWORD", "whatever")
16 | defer os.Setenv("SQLCMD_PASSWORD", "")
17 | }
18 |
19 | cmdparser.TestSetup(t)
20 | cmdparser.TestCmd[*AddUser]("--name user --username user --password-encryption none")
21 | cmdparser.TestCmd[*GetUsers]()
22 | cmdparser.TestCmd[*GetUsers]("user")
23 | }
24 |
25 | func TestNegGetUsers(t *testing.T) {
26 | cmdparser.TestSetup(t)
27 | assert.Panics(t, func() {
28 | cmdparser.TestCmd[*GetUsers]("does-not-exist")
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/use-context.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // UseContext implements the `sqlcmd config use-context` command
13 | type UseContext struct {
14 | cmdparser.Cmd
15 |
16 | name string
17 | }
18 |
19 | func (c *UseContext) DefineCommand(...cmdparser.CommandOptions) {
20 | options := cmdparser.CommandOptions{
21 | Use: "use-context",
22 | Short: localizer.Sprintf("Set the current context"),
23 | Examples: []cmdparser.ExampleOptions{{
24 | Description: localizer.Sprintf("Set the mssql context (endpoint/user) to be the current context"),
25 | Steps: []string{"sqlcmd config use-context mssql"}}},
26 | Aliases: []string{"use", "change-context", "set-context"},
27 | Run: c.run,
28 |
29 | FirstArgAlternativeForFlag: &cmdparser.AlternativeForFlagOptions{Flag: "name", Value: &c.name},
30 | }
31 |
32 | c.Cmd.DefineCommand(options)
33 |
34 | c.AddFlag(cmdparser.FlagOptions{
35 | String: &c.name,
36 | Name: "name",
37 | Usage: localizer.Sprintf("Name of context to set as current context")})
38 | }
39 |
40 | func (c *UseContext) run() {
41 | output := c.Output()
42 |
43 | if config.ContextExists(c.name) {
44 | config.SetCurrentContextName(c.name)
45 | output.InfoWithHints([]string{
46 | localizer.Sprintf("To run a query: %s", localizer.RunQueryExample),
47 | localizer.Sprintf("To remove: %s", localizer.UninstallCommand)},
48 | localizer.Sprintf("Switched to context \"%v\".", c.name))
49 | } else {
50 | output.FatalWithHints([]string{localizer.Sprintf("To view available contexts run `%s`", localizer.GetContextCommand)},
51 | localizer.Sprintf("No context exists with the name: \"%v\"", c.name))
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/use-context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestUseContext(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*AddEndpoint]()
15 | cmdparser.TestCmd[*AddContext]("--endpoint endpoint")
16 | cmdparser.TestCmd[*UseContext]("--name context")
17 | }
18 |
19 | func TestNegUseContext(t *testing.T) {
20 | cmdparser.TestSetup(t)
21 | assert.Panics(t, func() {
22 | cmdparser.TestCmd[*UseContext]("does-not-exist")
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/view.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // View implements the `sqlcmd config view` command
13 | type View struct {
14 | cmdparser.Cmd
15 |
16 | raw bool
17 | }
18 |
19 | func (c *View) DefineCommand(...cmdparser.CommandOptions) {
20 | options := cmdparser.CommandOptions{
21 | Use: "view",
22 | Short: localizer.Sprintf("Display merged sqlconfig settings or a specified sqlconfig file"),
23 | Examples: []cmdparser.ExampleOptions{
24 | {
25 | Description: localizer.Sprintf("Show sqlconfig settings, with REDACTED authentication data"),
26 | Steps: []string{"sqlcmd config view"},
27 | },
28 | {
29 | Description: localizer.Sprintf("Show sqlconfig settings and raw authentication data"),
30 | Steps: []string{"sqlcmd config view --raw"},
31 | },
32 | },
33 | Aliases: []string{"show"},
34 | Run: c.run,
35 | }
36 |
37 | c.Cmd.DefineCommand(options)
38 |
39 | c.AddFlag(cmdparser.FlagOptions{
40 | Name: "raw",
41 | Bool: &c.raw,
42 | Usage: localizer.Sprintf("Display raw byte data"),
43 | })
44 | }
45 |
46 | func (c *View) run() {
47 | output := c.Output()
48 |
49 | contents := config.RedactedConfig(c.raw)
50 | output.Struct(contents)
51 | }
52 |
--------------------------------------------------------------------------------
/cmd/modern/root/config/view_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | func TestView(t *testing.T) {
12 | cmdparser.TestSetup(t)
13 | cmdparser.TestCmd[*View]()
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/modern/root/config_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | // TestConfig runs a sanity test of `sqlcmd config`
12 | func TestConfig(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*Config]()
15 | }
16 |
--------------------------------------------------------------------------------
/cmd/modern/root/install.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/install"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // Install defines the `sqlcmd install` sub-commands
13 | type Install struct {
14 | cmdparser.Cmd
15 | }
16 |
17 | func (c *Install) DefineCommand(...cmdparser.CommandOptions) {
18 | options := cmdparser.CommandOptions{
19 | Use: "create",
20 | Short: localizer.Sprintf("Install/Create SQL Server, Azure SQL, and Tools"),
21 | Aliases: []string{"install"},
22 | SubCommands: c.SubCommands(),
23 | }
24 |
25 | c.Cmd.DefineCommand(options)
26 | }
27 |
28 | // SubCommands sets up the sub-commands for `sqlcmd install` such as
29 | // `sqlcmd install mssql` and `sqlcmd install azsql-edge`
30 | func (c *Install) SubCommands() []cmdparser.Command {
31 | dependencies := c.Dependencies()
32 |
33 | return []cmdparser.Command{
34 | cmdparser.New[*install.Mssql](dependencies),
35 | cmdparser.New[*install.Edge](dependencies),
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/edge.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package install
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/install/edge"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
10 | "github.com/microsoft/go-sqlcmd/internal/localizer"
11 | "github.com/microsoft/go-sqlcmd/internal/pal"
12 | )
13 |
14 | // Edge implements the `sqlcmd install azsql-edge command and sub-commands
15 | type Edge struct {
16 | cmdparser.Cmd
17 | MssqlBase
18 | }
19 |
20 | func (c *Edge) DefineCommand(...cmdparser.CommandOptions) {
21 | const repo = "azure-sql-edge"
22 |
23 | options := cmdparser.CommandOptions{
24 | Use: "azsql-edge",
25 | Short: localizer.Sprintf("Install Azure Sql Edge"),
26 | Examples: []cmdparser.ExampleOptions{{
27 | Description: localizer.Sprintf("Install/Create Azure SQL Edge in a container"),
28 | Steps: []string{"sqlcmd create azsql-edge"}}},
29 | Run: c.MssqlBase.Run,
30 | SubCommands: c.SubCommands(),
31 | }
32 |
33 | c.MssqlBase.SetCrossCuttingConcerns(dependency.Options{
34 | EndOfLine: pal.LineBreak(),
35 | Output: c.Output(),
36 | })
37 |
38 | c.Cmd.DefineCommand(options)
39 | c.AddFlags(c.AddFlag, repo, "edge")
40 | }
41 |
42 | func (c *Edge) SubCommands() []cmdparser.Command {
43 | return []cmdparser.Command{
44 | cmdparser.New[*edge.GetTags](c.Dependencies()),
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/edge/get-tags.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package edge
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/container"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | type GetTags struct {
13 | cmdparser.Cmd
14 | }
15 |
16 | func (c *GetTags) DefineCommand(...cmdparser.CommandOptions) {
17 | options := cmdparser.CommandOptions{
18 | Use: "get-tags",
19 | Short: localizer.Sprintf("Get tags available for Azure SQL Edge install"),
20 | Examples: []cmdparser.ExampleOptions{
21 | {
22 | Description: localizer.Sprintf("List tags"),
23 | Steps: []string{"sqlcmd create azsql-edge get-tags"},
24 | },
25 | },
26 | Aliases: []string{"gt", "lt"},
27 | Run: c.run,
28 | }
29 |
30 | c.Cmd.DefineCommand(options)
31 | }
32 |
33 | func (c *GetTags) run() {
34 | output := c.Output()
35 |
36 | tags := container.ListTags(
37 | "azure-sql-edge",
38 | "https://mcr.microsoft.com",
39 | )
40 | output.Struct(tags)
41 | }
42 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/edge/get-tags_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package edge
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | func TestEdgeGetTags(t *testing.T) {
12 | cmdparser.TestSetup(t)
13 | cmdparser.TestCmd[*GetTags]()
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/edge_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package install
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/install/edge"
9 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
10 | "github.com/microsoft/go-sqlcmd/internal/config"
11 | "github.com/microsoft/go-sqlcmd/internal/container"
12 | "github.com/stretchr/testify/assert"
13 | "testing"
14 | )
15 |
16 | func TestInstallEdge(t *testing.T) {
17 | // DEVNOTE: To prevent "import cycle not allowed" golang compile time error (due to
18 | // cleaning up the Install using root.Uninstall), we don't use root.Uninstall,
19 | // and use the controller object instead
20 |
21 | const registry = "docker.io"
22 | const repo = "library/hello-world"
23 |
24 | cmdparser.TestSetup(t)
25 | cmdparser.TestCmd[*edge.GetTags]()
26 | cmdparser.TestCmd[*Edge](
27 | fmt.Sprintf(
28 | `--accept-eula --user-database foo --errorlog-wait-line "Hello from Docker!" --registry %v --repo %v`,
29 | registry,
30 | repo))
31 |
32 | controller := container.NewController()
33 | id := config.ContainerId()
34 | err := controller.ContainerStop(id)
35 | assert.Nil(t, err)
36 | err = controller.ContainerRemove(id)
37 | assert.Nil(t, err)
38 | }
39 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/mssql/get-tags.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package mssql
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/container"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | type GetTags struct {
13 | cmdparser.Cmd
14 | }
15 |
16 | func (c *GetTags) DefineCommand(...cmdparser.CommandOptions) {
17 | options := cmdparser.CommandOptions{
18 | Use: "get-tags",
19 | Short: localizer.Sprintf("Get tags available for mssql install"),
20 | Examples: []cmdparser.ExampleOptions{
21 | {
22 | Description: localizer.Sprintf("List tags"),
23 | Steps: []string{"sqlcmd create mssql get-tags"},
24 | },
25 | },
26 | Aliases: []string{"gt", "lt"},
27 | Run: c.run,
28 | }
29 |
30 | c.Cmd.DefineCommand(options)
31 |
32 | }
33 |
34 | func (c *GetTags) run() {
35 | output := c.Output()
36 |
37 | tags := container.ListTags(
38 | "mssql/server",
39 | "https://mcr.microsoft.com",
40 | )
41 | output.Struct(tags)
42 | }
43 |
--------------------------------------------------------------------------------
/cmd/modern/root/install/mssql/get-tags_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package mssql
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | func TestEdgeGetTags(t *testing.T) {
12 | cmdparser.TestSetup(t)
13 | cmdparser.TestCmd[*GetTags]()
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/modern/root/install_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | // TestInstall runs a sanity test of `sqlcmd install`
12 | func TestInstall(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*Install]()
15 | }
16 |
--------------------------------------------------------------------------------
/cmd/modern/root/open.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/open"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // Open defines the `sqlcmd open` sub-commands
13 | type Open struct {
14 | cmdparser.Cmd
15 | }
16 |
17 | func (c *Open) DefineCommand(...cmdparser.CommandOptions) {
18 | options := cmdparser.CommandOptions{
19 | Use: "open",
20 | Short: localizer.Sprintf("Open tools (e.g Azure Data Studio) for current context"),
21 | SubCommands: c.SubCommands(),
22 | }
23 |
24 | c.Cmd.DefineCommand(options)
25 | }
26 |
27 | // SubCommands sets up the sub-commands for `sqlcmd open` such as
28 | // `sqlcmd open ads`
29 | func (c *Open) SubCommands() []cmdparser.Command {
30 | dependencies := c.Dependencies()
31 |
32 | return []cmdparser.Command{
33 | cmdparser.New[*open.Ads](dependencies),
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/cmd/modern/root/open/ads_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package open
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/localizer"
10 | )
11 |
12 | // Type Ads is used to implement the "open ads" which launches Azure
13 | // Data Studio and establishes a connection to the SQL Server for the current
14 | // context
15 | type Ads struct {
16 | cmdparser.Cmd
17 | }
18 |
19 | func (c *Ads) persistCredentialForAds(hostname string, endpoint sqlconfig.Endpoint, user *sqlconfig.User) {
20 | // UNDONE: See - https://github.com/microsoft/go-sqlcmd/issues/257
21 | }
22 |
23 | // BUG(stuartpa): There is a bug in ADS that is naming credentials in Mac KeyChain
24 | // using UTF16 encoding, when it should be UTF8. This prevents us from creating
25 | // an item in KeyChain that ADS can then re-use (because all the golang Keychain
26 | // packages take a string for credential name, which is UTF8). Rather than trying
27 | // to clone the ADS bug here, we prompt the user without to get the credential which
28 | // they'll have to enter into ADS (once, if they save password in the connection dialog)
29 | func (c *Ads) displayPreLaunchInfo() {
30 | output := c.Output()
31 |
32 | output.Info(localizer.Sprintf("Temporary: To view connection information run:"))
33 | output.Info("")
34 | output.Info("\tsqlcmd config connection-strings")
35 | output.Info("")
36 | output.Info("(see issue for more information: https://github.com/microsoft/go-sqlcmd/issues/257)")
37 | }
38 |
--------------------------------------------------------------------------------
/cmd/modern/root/open/ads_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package open
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | )
10 |
11 | // Type Ads is used to implement the "open ads" which launches Azure
12 | // Data Studio and establishes a connection to the SQL Server for the current
13 | // context
14 | type Ads struct {
15 | cmdparser.Cmd
16 | }
17 |
18 | func (c *Ads) persistCredentialForAds(hostname string, endpoint sqlconfig.Endpoint, user *sqlconfig.User) {
19 | panic("not implemented")
20 | }
21 |
22 | func (c *Ads) displayPreLaunchInfo() {
23 | panic("not implemented")
24 | }
25 |
--------------------------------------------------------------------------------
/cmd/modern/root/open/ads_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package open
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
9 | "github.com/microsoft/go-sqlcmd/internal/config"
10 | "runtime"
11 | "testing"
12 | )
13 |
14 | // TestOpen runs a sanity test of `sqlcmd open`
15 | func TestAds(t *testing.T) {
16 | if runtime.GOOS != "windows" {
17 | t.Skip("Ads support only on Windows at this time")
18 | }
19 |
20 | cmdparser.TestSetup(t)
21 | config.AddEndpoint(sqlconfig.Endpoint{
22 | AssetDetails: nil,
23 | EndpointDetails: sqlconfig.EndpointDetails{},
24 | Name: "endpoint",
25 | })
26 | config.AddContext(sqlconfig.Context{
27 | ContextDetails: sqlconfig.ContextDetails{
28 | Endpoint: "endpoint",
29 | User: nil,
30 | },
31 | Name: "context",
32 | })
33 | config.SetCurrentContextName("context")
34 |
35 | cmdparser.TestCmd[*Ads]()
36 | }
37 |
--------------------------------------------------------------------------------
/cmd/modern/root/open/ads_windows_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package open
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
9 | "github.com/microsoft/go-sqlcmd/internal/credman"
10 | "github.com/microsoft/go-sqlcmd/internal/output"
11 | "github.com/microsoft/go-sqlcmd/internal/secret"
12 | "github.com/stretchr/testify/assert"
13 | "testing"
14 | )
15 |
16 | func TestPersistCredentialForAds(t *testing.T) {
17 | ads := Ads{}
18 | ads.SetCrossCuttingConcerns(dependency.Options{
19 | EndOfLine: "",
20 | Output: output.New(output.Options{}),
21 | })
22 |
23 | user := &sqlconfig.User{
24 | BasicAuth: &sqlconfig.BasicAuthDetails{
25 | Username: "testuser",
26 | Password: "testpass",
27 | PasswordEncryption: "none",
28 | },
29 | }
30 | ads.persistCredentialForAds("localhost", sqlconfig.Endpoint{
31 | EndpointDetails: sqlconfig.EndpointDetails{
32 | Port: 1433,
33 | },
34 | }, user)
35 |
36 | // Test if the correct target name is generated
37 | expectedTargetName := "Microsoft.SqlTools|itemtype:Profile|id:providerName:MSSQL|applicationName:azdata|authenticationType:SqlLogin|database:|server:localhost,1433|user:testuser"
38 | assert.Equal(t, ads.credential.TargetName, expectedTargetName, "Expected target name to be %s, got %s", expectedTargetName, ads.credential.TargetName)
39 | assert.Equal(t, ads.credential.UserName, user.BasicAuth.Username, "Expected username to be %s, got %s", user.BasicAuth.Username, ads.credential.UserName)
40 |
41 | // Test if the password is decoded correctly
42 | decodedPassword := secret.DecodeAsUtf16(user.BasicAuth.Password, user.BasicAuth.PasswordEncryption)
43 | assert.Equal(
44 | t,
45 | ads.credential.CredentialBlob,
46 | decodedPassword,
47 | "Expected decoded password to be %v, got %v",
48 | decodedPassword,
49 | ads.credential.CredentialBlob,
50 | )
51 | }
52 |
53 | func TestRemovePreviousCredential(t *testing.T) {
54 | ads := Ads{}
55 | ads.SetCrossCuttingConcerns(dependency.Options{
56 | EndOfLine: "",
57 | Output: output.New(output.Options{}),
58 | })
59 |
60 | ads.credential = credman.Credential{
61 | TargetName: "TestTargetName",
62 | Persist: credman.PersistSession,
63 | }
64 |
65 | ads.writeCredential()
66 | ads.removePreviousCredential()
67 | }
68 |
--------------------------------------------------------------------------------
/cmd/modern/root/open_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "testing"
9 | )
10 |
11 | // TestOpen runs a sanity test of `sqlcmd open`
12 | func TestOpen(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | cmdparser.TestCmd[*Open]()
15 | }
16 |
--------------------------------------------------------------------------------
/cmd/modern/root/query_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/config"
9 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
10 | "os"
11 | "runtime"
12 | "testing"
13 | )
14 |
15 | // TestQuery runs a sanity test of `sqlcmd query` using the local instance on 1433
16 | func TestQuery(t *testing.T) {
17 | if runtime.GOOS != "windows" {
18 | t.Skip("stuartpa: This is failing in the pipeline (Login failed for user 'sa'.)")
19 | }
20 |
21 | cmdparser.TestSetup(t)
22 |
23 | setupContext(t)
24 | cmdparser.TestCmd[*Query]("PRINT")
25 | }
26 |
27 | func TestQueryWithNonDefaultDatabase(t *testing.T) {
28 | if runtime.GOOS != "windows" {
29 | t.Skip("stuartpa: This is failing in the pipeline (Login failed for user 'sa'.)")
30 | }
31 |
32 | cmdparser.TestSetup(t)
33 |
34 | setupContext(t)
35 | cmdparser.TestCmd[*Query](`--text "PRINT DB_NAME()" --database master`)
36 |
37 | // TODO: Add test validation that DB name was actually master!
38 | }
39 |
40 | func setupContext(t *testing.T) {
41 | // if SQLCMDSERVER != "" add an endpoint using the --address
42 | if os.Getenv("SQLCMDSERVER") == "" {
43 | cmdparser.TestCmd[*config.AddEndpoint]()
44 | } else {
45 | t.Logf("SQLCMDSERVER: %v", os.Getenv("SQLCMDSERVER"))
46 | cmdparser.TestCmd[*config.AddEndpoint](fmt.Sprintf("--address %v", os.Getenv("SQLCMDSERVER")))
47 | }
48 |
49 | // If the SQLCMDPASSWORD envvar is set, then add a basic user, otherwise
50 | // we'll use trusted auth
51 | if os.Getenv("SQLCMDPASSWORD") != "" &&
52 | os.Getenv("SQLCMDUSER") != "" {
53 |
54 | cmdparser.TestCmd[*config.AddUser](
55 | fmt.Sprintf("--name user1 --username %s",
56 | os.Getenv("SQLCMDUSER")))
57 | cmdparser.TestCmd[*config.AddContext]("--endpoint endpoint --user user1")
58 | } else {
59 | cmdparser.TestCmd[*config.AddContext]("--endpoint endpoint")
60 | }
61 | cmdparser.TestCmd[*config.View]() // displaying the config (info in-case test fails)
62 | }
63 |
--------------------------------------------------------------------------------
/cmd/modern/root/start.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/container"
10 | "github.com/microsoft/go-sqlcmd/internal/localizer"
11 | )
12 |
13 | type Start struct {
14 | cmdparser.Cmd
15 | }
16 |
17 | func (c *Start) DefineCommand(...cmdparser.CommandOptions) {
18 | options := cmdparser.CommandOptions{
19 | Use: "start",
20 | Short: localizer.Sprintf("Start current context"),
21 | Examples: []cmdparser.ExampleOptions{
22 | {
23 | Description: localizer.Sprintf("Start the current context"),
24 | Steps: []string{`sqlcmd start`}},
25 | },
26 | Run: c.run,
27 | }
28 |
29 | c.Cmd.DefineCommand(options)
30 | }
31 |
32 | func (c *Start) run() {
33 | output := c.Output()
34 |
35 | if config.CurrentContextName() == "" {
36 | output.FatalWithHintExamples([][]string{
37 | {localizer.Sprintf("To view available contexts"), "sqlcmd config get-contexts"},
38 | }, localizer.Sprintf("No current context"))
39 | }
40 | if config.CurrentContextEndpointHasContainer() {
41 | controller := container.NewController()
42 | id := config.ContainerId()
43 | endpoint, _ := config.CurrentContext()
44 |
45 | output.Info(localizer.Sprintf("Starting %q for context %q", endpoint.ContainerDetails.Image, config.CurrentContextName()))
46 | err := controller.ContainerStart(id)
47 | c.CheckErr(err)
48 | } else {
49 | output.FatalWithHintExamples([][]string{
50 | {localizer.Sprintf("Create new context with a sql container "), "sqlcmd create mssql"},
51 | }, localizer.Sprintf("Current context does not have a container"))
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/cmd/modern/root/start_test.go:
--------------------------------------------------------------------------------
1 | package root
2 |
3 | import (
4 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/config"
5 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
6 | "github.com/stretchr/testify/assert"
7 | "testing"
8 | )
9 |
10 | // TestNegStart tests that the `sqlcmd start` command fails when
11 | // no context is defined
12 | func TestNegStart(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | assert.Panics(t, func() {
15 | cmdparser.TestCmd[*Start]()
16 | })
17 | }
18 |
19 | // TestNegStart2 tests that the `sqlcmd start` command fails when
20 | // no container is included in endpoint
21 | func TestNegStart2(t *testing.T) {
22 | cmdparser.TestSetup(t)
23 | cmdparser.TestCmd[*config.AddEndpoint]()
24 | cmdparser.TestCmd[*config.AddContext]("--endpoint endpoint")
25 | assert.Panics(t, func() {
26 | cmdparser.TestCmd[*Start]()
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/modern/root/stop.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/config"
9 | "github.com/microsoft/go-sqlcmd/internal/container"
10 | "github.com/microsoft/go-sqlcmd/internal/localizer"
11 | )
12 |
13 | type Stop struct {
14 | cmdparser.Cmd
15 | }
16 |
17 | func (c *Stop) DefineCommand(...cmdparser.CommandOptions) {
18 | options := cmdparser.CommandOptions{
19 | Use: "stop",
20 | Short: localizer.Sprintf("Stop current context"),
21 | Examples: []cmdparser.ExampleOptions{
22 | {
23 | Description: localizer.Sprintf("Stop the current context"),
24 | Steps: []string{`sqlcmd stop`}},
25 | },
26 | Run: c.run,
27 | }
28 |
29 | c.Cmd.DefineCommand(options)
30 | }
31 |
32 | func (c *Stop) run() {
33 | output := c.Output()
34 |
35 | if config.CurrentContextName() == "" {
36 | output.FatalWithHintExamples([][]string{
37 | {localizer.Sprintf("To view available contexts"), "sqlcmd config get-contexts"},
38 | }, localizer.Sprintf("No current context"))
39 | }
40 | if config.CurrentContextEndpointHasContainer() {
41 | controller := container.NewController()
42 | id := config.ContainerId()
43 | endpoint, _ := config.CurrentContext()
44 |
45 | output.Info(localizer.Sprintf("Stopping %q for context %q", endpoint.ContainerDetails.Image, config.CurrentContextName()))
46 | err := controller.ContainerStop(id)
47 | c.CheckErr(err)
48 | } else {
49 | output.FatalWithHintExamples([][]string{
50 | {localizer.Sprintf("Create a new context with a SQL Server container "), "sqlcmd create mssql"},
51 | }, localizer.Sprintf("Current context does not have a container"))
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/cmd/modern/root/stop_test.go:
--------------------------------------------------------------------------------
1 | package root
2 |
3 | import (
4 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/config"
5 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
6 | "github.com/stretchr/testify/assert"
7 | "testing"
8 | )
9 |
10 | // TestNegStop tests that the `sqlcmd stop` command fails when
11 | // no context is defined
12 | func TestNegStop(t *testing.T) {
13 | cmdparser.TestSetup(t)
14 | assert.Panics(t, func() {
15 | cmdparser.TestCmd[*Stop]()
16 | })
17 | }
18 |
19 | // TestNegStop2 tests that the `sqlcmd stop` command fails when
20 | // no container is included in endpoint
21 | func TestNegStop2(t *testing.T) {
22 | cmdparser.TestSetup(t)
23 | cmdparser.TestCmd[*config.AddEndpoint]()
24 | cmdparser.TestCmd[*config.AddContext]("--endpoint endpoint")
25 | assert.Panics(t, func() {
26 | cmdparser.TestCmd[*Stop]()
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/modern/root/uninstall_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package root
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/cmd/modern/root/install"
9 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
10 | "testing"
11 | )
12 |
13 | // TestUninstallWithUserDbPresent installs Mssql
14 | // with a user database, and then uninstalls it using the --force option
15 | func TestUninstallWithUserDbPresent(t *testing.T) {
16 | const registry = "docker.io"
17 | const repo = "library/hello-world"
18 |
19 | cmdparser.TestSetup(t)
20 |
21 | cmdparser.TestCmd[*install.Edge](
22 | fmt.Sprintf(
23 | `--accept-eula --port 1500 --errorlog-wait-line "Hello from Docker!" --registry %v --repo %v`,
24 | registry,
25 | repo))
26 | cmdparser.TestCmd[*Stop]()
27 | cmdparser.TestCmd[*Start]()
28 | cmdparser.TestCmd[*Uninstall]("--yes --force")
29 | }
30 |
--------------------------------------------------------------------------------
/cmd/modern/root_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
9 | "github.com/stretchr/testify/assert"
10 | "testing"
11 | )
12 |
13 | // TestRoot is a quick sanity test
14 | func TestRoot(t *testing.T) {
15 | c := cmdparser.New[*Root](dependency.Options{})
16 | c.DefineCommand()
17 | c.SetArgsForUnitTesting([]string{})
18 | c.Execute()
19 | }
20 |
21 | func TestIsValidSubCommand(t *testing.T) {
22 | c := cmdparser.New[*Root](dependency.Options{})
23 | invalid := c.IsValidSubCommand("nope")
24 | assert.Equal(t, false, invalid)
25 | valid := c.IsValidSubCommand("query")
26 | assert.Equal(t, true, valid)
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/modern/sqlconfig/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlconfig
5 |
6 | /*
7 | Package sqlconfig defines the schema for the sqlconfig file. The sqlconfig file
8 | by default resides in the folder:
9 |
10 | Windows: %USERPROFILE%\.sqlcmd\sqlconfig
11 | *nix: ~/.sqlcmd/sqlconfig
12 |
13 | The sqlconfig contains Contexts. A context is named (e.g. mssql2) and
14 | contains the Endpoint details (to connect to) and User details (to
15 | use for authentication with the endpoint.
16 |
17 | If there is more than one context defined, there is always a "currentcontext",
18 | the currentcontext can be changed using
19 |
20 | sqlcmd config use-context CONTEXT_NAME
21 |
22 | # Example
23 |
24 | An example of the sqlconfig file looks like this:
25 |
26 | apiversion: v1
27 | endpoints:
28 | - asset:
29 | - container:
30 | id: 0e698e65e19d9c
31 | image: mcr.microsoft.com/mssql/server:2022-latest
32 | endpoint:
33 | address: 127.0.0.1
34 | port: 1435
35 | name: mssql
36 | contexts:
37 | - context:
38 | endpoint: mssql
39 | user: your-alias@mssql
40 | name: mssql
41 | currentcontext: mssql
42 | kind: Config
43 | users:
44 | - user:
45 | username: your-alias
46 | password: REDACTED
47 | name: your-alias@mssql
48 |
49 | # Security
50 |
51 | - OnWindows the password is encrypted using the DPAPI.
52 | - TODO: On MacOS the password will be encrypted using the KeyChain
53 |
54 | The password is also base64 encoded.
55 |
56 | To view the decrypted and (base64) decoded passwords run
57 |
58 | sqlcmd config view --raw
59 | */
60 |
--------------------------------------------------------------------------------
/cmd/modern/sqlconfig/sqlconfig.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | // Package sqlconfig, defines the metadata for representing a sqlconfig file.
5 | // It includes structs for representing an endpoint, context, user, and the overall
6 | // sqlconfig file itself. Each struct has fields for storing the various pieces
7 | // of information that make up an SQL configuration, such as endpoint address
8 | // and port, context name and endpoint, and user authentication type and details.
9 | // These structs are used to manage and manipulate the sqlconfig.
10 | package sqlconfig
11 |
12 | type EndpointDetails struct {
13 | Address string `mapstructure:"address"`
14 | Port int `mapstructure:"port"`
15 | }
16 |
17 | type ContainerDetails struct {
18 | Id string `mapstructure:"id"`
19 | Image string `mapstructure:"image"`
20 | }
21 |
22 | type AssetDetails struct {
23 | *ContainerDetails `mapstructure:"container,omitempty" yaml:"container,omitempty"`
24 | }
25 |
26 | type Endpoint struct {
27 | *AssetDetails `mapstructure:"asset,omitempty" yaml:"asset,omitempty"`
28 | EndpointDetails `mapstructure:"endpoint" yaml:"endpoint"`
29 | Name string `mapstructure:"name"`
30 | }
31 |
32 | type ContextDetails struct {
33 | Endpoint string `mapstructure:"endpoint"`
34 | User *string `mapstructure:"user,omitempty" yaml:"user,omitempty"`
35 | }
36 |
37 | type Context struct {
38 | ContextDetails `mapstructure:"context" yaml:"context"`
39 | Name string `mapstructure:"name"`
40 | }
41 |
42 | type BasicAuthDetails struct {
43 | Username string `mapstructure:"username"`
44 | PasswordEncryption string `mapstructure:"password-encryption" yaml:"password-encryption"`
45 | Password string `mapstructure:"password"`
46 | }
47 |
48 | type User struct {
49 | Name string `mapstructure:"name"`
50 | AuthenticationType string `mapstructure:"authentication-type" yaml:"authentication-type"`
51 | BasicAuth *BasicAuthDetails `mapstructure:"basic-auth,omitempty" yaml:"basic-auth,omitempty"`
52 | }
53 |
54 | type Sqlconfig struct {
55 | Version string `mapstructure:"version"`
56 | Endpoints []Endpoint `mapstructure:"endpoints"`
57 | Contexts []Context `mapstructure:"contexts"`
58 | CurrentContext string `mapstructure:"currentcontext"`
59 | Users []User `mapstructure:"users"`
60 | }
61 |
--------------------------------------------------------------------------------
/cmd/modern/winres/winres.json:
--------------------------------------------------------------------------------
1 | {
2 | "RT_GROUP_ICON": {
3 | "APP": {
4 | "0000" : "../../../release/windows/msi/resources/sqlcmd.ico"
5 | }
6 | },
7 | "RT_VERSION": {
8 | "#1": {
9 | "0000": {
10 | "fixed": {
11 | "file_version": "0.0.0.0",
12 | "product_version": "0.0.0.0"
13 | },
14 | "info": {
15 | "0409": {
16 | "Comments": "SQL",
17 | "CompanyName": "Microsoft Corporation",
18 | "FileDescription": "T-SQL execution command line utility",
19 | "FileVersion": "",
20 | "InternalName": "go-sqlcmd",
21 | "LegalCopyright": "Microsoft Corporation",
22 | "LegalTrademarks": "Microsoft SQL Server is a registered trademark of Microsoft Corporation",
23 | "OriginalFilename": "sqlcmd.exe",
24 | "PrivateBuild": "",
25 | "ProductName": "Microsoft SQL Server",
26 | "ProductVersion": "",
27 | "SpecialBuild": ""
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/cmd/sqlcmd-linter/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | sqlcmdlinter "github.com/microsoft/go-sqlcmd/pkg/sqlcmd-linter"
5 | "golang.org/x/tools/go/analysis/multichecker"
6 | )
7 |
8 | func main() {
9 | multichecker.Main(sqlcmdlinter.AssertAnalyzer, sqlcmdlinter.ImportsAnalyzer)
10 | }
11 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/pipe_detection_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | import (
7 | "os"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | func TestStdinPipeDetection(t *testing.T) {
14 | // Get stdin info
15 | fi, err := os.Stdin.Stat()
16 | assert.NoError(t, err, "os.Stdin.Stat()")
17 |
18 | // On most CI systems, stdin will be a pipe or file (not a terminal)
19 | // We're testing the logic, not expecting a specific result
20 | isPipe := false
21 | if fi != nil && (fi.Mode()&os.ModeCharDevice) == 0 {
22 | isPipe = true
23 | }
24 |
25 | // Just making sure the detection code doesn't crash
26 | // The actual value will depend on the environment
27 | t.Logf("Stdin detected as pipe: %v", isPipe)
28 | }
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/bad.sql:
--------------------------------------------------------------------------------
1 | select @@badbad
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/create100db.sql:
--------------------------------------------------------------------------------
1 | create database db100
2 | go
3 |
4 | create database db101
5 | go
6 |
7 | create database db102
8 | go
9 | create database db103
10 | go
11 | create database db104
12 | go
13 | create database db105
14 | go
15 | create database db106
16 | go
17 | create database db107
18 | go
19 | create database db108
20 | go
21 | create database db109
22 | go
23 | create database db110
24 | go
25 | create database db111
26 | go
27 | create database db112
28 | go
29 | create database db113
30 | go
31 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/drop100db.txt:
--------------------------------------------------------------------------------
1 | drop database db100
2 | go
3 |
4 | drop database db101
5 | go
6 |
7 | drop database db102
8 | go
9 | drop database db103
10 | go
11 | drop database db104
12 | go
13 | drop database db105
14 | go
15 | drop database db106
16 | go
17 | drop database db107
18 | go
19 | drop database db108
20 | go
21 | drop database db109
22 | go
23 | drop database db110
24 | go
25 | drop database db111
26 | go
27 | drop database db112
28 | go
29 | drop database db113
30 | go
31 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/select,100.sql:
--------------------------------------------------------------------------------
1 | select 100
2 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/select100.sql:
--------------------------------------------------------------------------------
1 | select 100
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/selectunicode_BE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/cmd/sqlcmd/testdata/selectunicode_BE.txt
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/selectunicode_LE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/cmd/sqlcmd/testdata/selectunicode_LE.txt
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/selectutf8.txt:
--------------------------------------------------------------------------------
1 | select N'挨挨唉哀皑癌蔼矮' as SimplifiedChinese
2 |
3 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/selectutf8_bom.txt:
--------------------------------------------------------------------------------
1 | select N'挨挨唉哀皑癌蔼矮' as SimplifiedChinese
2 |
3 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/unicodeout.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/cmd/sqlcmd/testdata/unicodeout.txt
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/unicodeout_linux.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/cmd/sqlcmd/testdata/unicodeout_linux.txt
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/utf8out.txt:
--------------------------------------------------------------------------------
1 | SimplifiedChinese
2 | -----------------
3 | 挨挨唉哀皑癌蔼矮
4 |
5 | (1 row affected)
6 |
--------------------------------------------------------------------------------
/cmd/sqlcmd/testdata/utf8out_linux.txt:
--------------------------------------------------------------------------------
1 | SimplifiedChinese
2 | -----------------
3 | 挨挨唉哀皑癌蔼矮
4 |
5 | (1 row affected)
6 |
--------------------------------------------------------------------------------
/internal/buffer/memory-buffer.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package buffer
5 |
6 | import "bytes"
7 |
8 | // MemoryBuffer has both Write and Close methods for use as io.WriteCloser
9 | // when testing (instead of os.Stdout), so tests can assert.Equal results etc.
10 | type MemoryBuffer struct {
11 | buf *bytes.Buffer
12 | }
13 |
14 | func (b *MemoryBuffer) Write(p []byte) (n int, err error) {
15 | return b.buf.Write(p)
16 | }
17 |
18 | func (b *MemoryBuffer) Close() error {
19 | b.buf = nil
20 |
21 | return nil
22 | }
23 |
24 | func (b *MemoryBuffer) String() string {
25 | return b.buf.String()
26 | }
27 |
28 | func NewMemoryBuffer() *MemoryBuffer {
29 | return &MemoryBuffer{buf: new(bytes.Buffer)}
30 | }
31 |
--------------------------------------------------------------------------------
/internal/buffer/memory-buffer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package buffer
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | func TestMemoryBuffer_Write(t *testing.T) {
13 | buffer := NewMemoryBuffer()
14 | _, err := buffer.Write([]byte("hello world"))
15 | assert.NoError(t, err)
16 | assert.Equal(t, "hello world", buffer.String())
17 | }
18 |
19 | func TestMemoryBuffer_Close(t *testing.T) {
20 | buffer := NewMemoryBuffer()
21 | assert.NoError(t, buffer.Close())
22 | }
23 |
24 | func TestMemoryBuffer_String(t *testing.T) {
25 | buffer := NewMemoryBuffer()
26 | _, err := buffer.Write([]byte("foo bar"))
27 | if err != nil {
28 | return
29 | }
30 | assert.Equal(t, "foo bar", buffer.String())
31 | }
32 |
--------------------------------------------------------------------------------
/internal/cmdparser/dependency/options.go:
--------------------------------------------------------------------------------
1 | package dependency
2 |
3 | import "github.com/microsoft/go-sqlcmd/internal/output"
4 |
5 | type Options struct {
6 | EndOfLine string
7 | Output *output.Output
8 | }
9 |
--------------------------------------------------------------------------------
/internal/cmdparser/factory.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package cmdparser
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
9 | "github.com/microsoft/go-sqlcmd/internal/output"
10 | "github.com/spf13/cobra"
11 | "os"
12 | )
13 |
14 | // Initialize runs the init func() after the command-line provided by the user
15 | // has been parsed.
16 | func Initialize(init func()) {
17 | cobra.OnInitialize(init)
18 | }
19 |
20 | func New[T PtrAsReceiverWrapper[pointerType], pointerType any](dependencies dependency.Options) (command T) {
21 | if dependencies.Output == nil {
22 | dependencies.Output = output.New(output.Options{
23 | OutputType: "yaml",
24 | LoggingLevel: 2,
25 | StandardWriter: os.Stdout,
26 | ErrorHandler: func(err error) {
27 | if err != nil {
28 | panic(err)
29 | }
30 | },
31 | HintHandler: func(hints []string) { fmt.Printf("HINTS: %v\n", hints) }})
32 | }
33 | if dependencies.EndOfLine == "" {
34 | dependencies.EndOfLine = "\n"
35 | }
36 |
37 | command = new(pointerType)
38 | command.SetCrossCuttingConcerns(dependencies)
39 | command.DefineCommand()
40 |
41 | return
42 | }
43 |
44 | // PtrAsReceiverWrapper per golang design doc "an unfortunate necessary kludge":
45 | // https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#pointer-method-example
46 | // https://www.reddit.com/r/golang/comments/uqwh5d/generics_new_value_from_pointer_type_with/
47 | type PtrAsReceiverWrapper[T any] interface {
48 | Command
49 | *T
50 | }
51 |
--------------------------------------------------------------------------------
/internal/cmdparser/interface.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package cmdparser
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | // Command is an interface for defining and running a command which is
12 | // part of a command line program. Command contains methods for setting
13 | // command options, running the command, and checking for errors.
14 | type Command interface {
15 | // CheckErr checks if the given error is non-nil and, if it is, it prints the error
16 | // to the output and exits the program with an exit code of 1.
17 | CheckErr(error)
18 |
19 | // Command returns the underlying cobra.Command object for this command.
20 | // This is useful for defining subcommands.
21 | Command() *cobra.Command
22 |
23 | // DefineCommand is used to define a new command and its associated
24 | // options, flags, and subcommands. It takes in a CommandOptions
25 | // struct, which allow the caller to specify the command's name, description,
26 | // usage, and behavior.
27 | DefineCommand(...CommandOptions)
28 |
29 | // IsSubCommand is TEMPORARY code that will be removed when the
30 | // old Kong CLI is retired. It returns true if the command-line
31 | // provided by the user looks like they want the new cobra CLI, e.g.
32 | // sqlcmd query, sqlcmd install, sqlcmd --help etc.
33 | IsSubCommand(command string) bool
34 |
35 | // SetArgsForUnitTesting method allows a caller to set the arguments for the
36 | // command when running unit tests. This is useful because it allows the caller
37 | // to simulate different command-line input scenarios in their tests.
38 | SetArgsForUnitTesting(args []string)
39 |
40 | // SetCrossCuttingConcerns is used to inject cross-cutting concerns (i.e. dependencies)
41 | // into the Command object (like logging etc.). The dependency.Options allows
42 | // the Command object to have access to the dependencies it needs, without
43 | // having to manage them directly.
44 | SetCrossCuttingConcerns(dependency.Options)
45 | }
46 |
--------------------------------------------------------------------------------
/internal/cmdparser/test_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package cmdparser
5 |
6 | // Test_test.go contains functions to test the functions in test.go, so this file
7 | // tests the test functions. This file shows end-to-end usage of how to create
8 | // the simplest command-line application and run it
9 |
10 | import (
11 | "errors"
12 | "github.com/stretchr/testify/assert"
13 | "testing"
14 | )
15 |
16 | type TestCommand struct {
17 | Cmd
18 |
19 | throwError string
20 | }
21 |
22 | func (c *TestCommand) DefineCommand(...CommandOptions) {
23 | options := CommandOptions{}
24 | options.Use = "test-cmd"
25 | options.Short = "A test command"
26 | options.FirstArgAlternativeForFlag = &AlternativeForFlagOptions{
27 | Flag: "throw-error",
28 | Value: &c.throwError,
29 | }
30 | options.Run = func() {
31 | c.Output().InfofWithHints([]string{"This is a hint"}, "Some things to consider")
32 |
33 | if c.throwError == "throw-error" {
34 | c.CheckErr(errors.New("Expected error"))
35 | }
36 | }
37 |
38 | c.Cmd.DefineCommand(options)
39 | c.AddFlag(FlagOptions{Name: "throw-error", Usage: "Throw an error", String: &c.throwError})
40 | }
41 |
42 | func TestTest(t *testing.T) {
43 | TestSetup(t)
44 | TestCmd[*TestCommand]()
45 | }
46 |
47 | func TestTest2(t *testing.T) {
48 | TestSetup(t)
49 | TestCmd[*TestCommand]("test-cmd")
50 | }
51 |
52 | func TestThrowError(t *testing.T) {
53 | TestSetup(t)
54 |
55 | assert.Panics(t, func() {
56 | TestCmd[*TestCommand]("throw-error")
57 | })
58 | }
59 |
60 | func TestTest3(t *testing.T) {
61 | TestSetup(t)
62 | }
63 |
--------------------------------------------------------------------------------
/internal/cmdparser/type.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package cmdparser
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | // Cmd is the main type used for defining and running command line programs.
12 | // It contains fields and methods for defining the command, setting its options,
13 | // and running the command.
14 | type Cmd struct {
15 | dependencies dependency.Options
16 | options CommandOptions
17 | command cobra.Command
18 | unitTesting bool
19 | }
20 |
--------------------------------------------------------------------------------
/internal/config/endpoint_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func TestEndpointExists(t *testing.T) {
12 | assert.Panics(t, func() { EndpointExists("") })
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/internal/config/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/config/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/config/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/internal/net"
9 | "github.com/microsoft/go-sqlcmd/internal/secret"
10 | )
11 |
12 | var encryptCallback func(plainText string, encryptionMethod string) (cipherText string)
13 | var decryptCallback func(cipherText string, encryptionMethod string) (secret string)
14 | var isLocalPortAvailableCallback func(port int) (portAvailable bool)
15 |
16 | // init sets up the package to work with a set of handlers to be used for the period
17 | // before the command-line has been parsed
18 | func init() {
19 | errorHandler := func(err error) {
20 | if err != nil {
21 | panic(err)
22 | }
23 | }
24 | traceHandler := func(format string, a ...any) {
25 | fmt.Printf(format, a...)
26 | }
27 |
28 | Initialize(
29 | errorHandler,
30 | traceHandler,
31 | secret.Encode,
32 | secret.Decode,
33 | net.IsLocalPortAvailable)
34 | }
35 |
36 | // Initialize sets the callback functions used by the config package.
37 | // These callback functions are used for logging errors, tracing debug messages,
38 | // encrypting and decrypting data, and checking if a local port is available.
39 | // The callback functions are passed to the function as arguments.
40 | // This function should be called at the start of the application to ensure that the
41 | // config package has the necessary callback functions available.
42 | func Initialize(
43 | errorHandler func(err error),
44 | traceHandler func(format string, a ...any),
45 | encryptHandler func(plainText string, encryptionMethod string) (cipherText string),
46 | decryptHandler func(cipherText string, encryptionMethod string) (secret string),
47 | isLocalPortAvailableHandler func(port int) (portAvailable bool),
48 | ) {
49 | errorCallback = errorHandler
50 | traceCallback = traceHandler
51 | encryptCallback = encryptHandler
52 | decryptCallback = decryptHandler
53 | isLocalPortAvailableCallback = isLocalPortAvailableHandler
54 | }
55 |
--------------------------------------------------------------------------------
/internal/config/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/config/user_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestAddUser(t *testing.T) {
13 | assert.Panics(t, func() {
14 | AddUser(User{
15 | Name: "",
16 | AuthenticationType: "basic",
17 | BasicAuth: nil,
18 | })
19 | })
20 | }
21 |
22 | func TestUserExists2(t *testing.T) {
23 | context := Context{
24 | ContextDetails: ContextDetails{},
25 | Name: "context",
26 | }
27 | assert.False(t, UserExists(context))
28 | }
29 |
30 | func TestUserExists3(t *testing.T) {
31 | user := "user"
32 | context := Context{
33 | ContextDetails: ContextDetails{User: &user},
34 | Name: "context",
35 | }
36 | assert.True(t, UserExists(context))
37 | }
38 |
39 | func TestNegAddUser(t *testing.T) {
40 | assert.Panics(t, func() {
41 | AddUser(User{
42 | Name: "",
43 | AuthenticationType: "basic",
44 | BasicAuth: &BasicAuthDetails{
45 | Username: "",
46 | PasswordEncryption: "none",
47 | Password: "",
48 | },
49 | })
50 | })
51 | }
52 |
53 | func TestNegAddUser2(t *testing.T) {
54 | assert.Panics(t, func() {
55 | GetUser("doesnotexist")
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/internal/config/viper_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package config
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func Test_configureViper(t *testing.T) {
12 | assert.Panics(t, func() {
13 | configureViper("")
14 | })
15 | }
16 |
17 | func Test_Load(t *testing.T) {
18 | SetFileNameForTest(t)
19 | Clean()
20 | Load()
21 | }
22 |
23 | func TestNeg_Load(t *testing.T) {
24 | filename = ""
25 | assert.Panics(t, func() {
26 | Load()
27 | })
28 | }
29 |
30 | func TestNeg_Save(t *testing.T) {
31 | filename = ""
32 | assert.Panics(t, func() {
33 | Save()
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/internal/container/docker.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package container
5 |
6 | import (
7 | "context"
8 | "github.com/docker/distribution/reference"
9 | "github.com/docker/distribution/registry/client"
10 | "net/http"
11 | )
12 |
13 | // ListTags lists all tags for a container image located at a given
14 | // path in the container registry. It takes the path to the image and the
15 | // URL of the registry as input and returns a slice of strings containing
16 | // the tags.
17 | func ListTags(path string, baseURL string) []string {
18 | ctx := context.Background()
19 | repo, err := reference.WithName(path)
20 | checkErr(err)
21 | repository, err := client.NewRepository(
22 | repo,
23 | baseURL,
24 | http.DefaultTransport,
25 | )
26 | checkErr(err)
27 | tagService := repository.Tags(ctx)
28 | tags, err := tagService.All(ctx)
29 | checkErr(err)
30 |
31 | return tags
32 | }
33 |
--------------------------------------------------------------------------------
/internal/container/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package container
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/container/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package container
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/container/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package container
5 |
6 | func init() {
7 | Initialize(
8 | func(err error) {
9 | if err != nil {
10 | panic(err)
11 | }
12 | },
13 | func(format string, a ...any) {})
14 | }
15 |
16 | func Initialize(
17 | errorHandler func(err error),
18 | traceHandler func(format string, a ...any)) {
19 | errorCallback = errorHandler
20 | traceCallback = traceHandler
21 | }
22 |
--------------------------------------------------------------------------------
/internal/container/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package container
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/credman/credman_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package credman
5 |
--------------------------------------------------------------------------------
/internal/credman/types_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package credman
5 |
6 | import (
7 | syscall "golang.org/x/sys/windows"
8 | "time"
9 | )
10 |
11 | const (
12 | CredTypeGeneric CredentialType = 0x1
13 | )
14 |
15 | // Credential is the basic credential structure.
16 | // A credential is identified by its target name.
17 | // The actual credential secret is available in the CredentialBlob field.
18 | type Credential struct {
19 | TargetName string
20 | Comment string
21 | LastWritten time.Time
22 | CredentialBlob []byte
23 | TargetAlias string
24 | UserName string
25 | Persist CredentialPersistence
26 | }
27 |
28 | // CredentialPersistence describes one of three persistence modes of a credential.
29 | // A detailed description of the available modes can be found on:
30 | //
31 | // https://learn.microsoft.com/windows/win32/api/wincred/ns-wincred-credentiala
32 | type CredentialPersistence uint32
33 |
34 | const (
35 | // PersistSession indicates that the credential only persists for the life
36 | // of the current Windows login session. Such a credential is not visible in
37 | // any other logon session, even from the same user.
38 | PersistSession CredentialPersistence = 0x1
39 | )
40 |
41 | // CredentialAttribute represents an application-specific attribute of a credential.
42 | type CredentialAttribute struct {
43 | Keyword string
44 | Value []byte
45 | }
46 |
47 | // Interface for syscall.Proc
48 | type proc interface {
49 | Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
50 | }
51 |
52 | type CREDENTIAL struct {
53 | Flags uint32
54 | Type uint32
55 | TargetName *uint16
56 | Comment *uint16
57 | LastWritten syscall.Filetime
58 | CredentialBlobSize uint32
59 | CredentialBlob uintptr
60 | Persist uint32
61 | AttributeCount uint32
62 | Attributes uintptr
63 | TargetAlias *uint16
64 | UserName *uint16
65 | }
66 |
67 | type CREDENTIAL_ATTRIBUTE struct {
68 | Keyword *uint16
69 | Flags uint32
70 | ValueSize uint32
71 | Value uintptr
72 | }
73 |
74 | type CredentialType uint32
75 |
--------------------------------------------------------------------------------
/internal/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package internal
5 |
6 | /*
7 | These internal packages abstract the following from the application (using
8 | dependency injection):
9 |
10 | - error handling (for non-control flow)
11 | - trace support (non-localized output)
12 |
13 | The above abstractions enable application code to not have to sprinkle
14 | if (err != nil) blocks (except when the application wants to affect application
15 | flow based on err)
16 |
17 | Do and Do Not:
18 | - Do verify parameter values and panic if these internal functions would be unable
19 | to succeed, to catch coding errors (do not panic for user input errors)
20 | - Do not output (except for in the `internal/output` package). Do use the injected
21 | trace method to output low level debugging information
22 | - Do not return error if client is not going use the error for control flow, call the
23 | injected checkErr instead, which will probably end up calling cobra.checkErr and exit:
24 | e.g. Do not sprinkle application (non-helper) code with:
25 | err, _ := fmt.printf("Hope this works")
26 | if (err != nil) {
27 | panic("How unlikely")
28 | }
29 | Do use the injected checkErr callback and let the application decide what to do
30 | err, _ := printf("Hope this works)
31 | checkErr(err)
32 | - Do not have an internal package take a dependency on another internal package
33 | unless they are building on each other, instead inject the needed capability in the
34 | internal.initiaize()
35 | e.g. Do not have the config package take a dependency on the secret package, instead
36 | inject the methods encrypt/decrypt to config in its initialize method, do not:
37 |
38 | package config
39 |
40 | import (
41 | "github.com/microsoft/go-sqlcmd/cmd/internal/secret"
42 | )
43 |
44 | Do instead:
45 |
46 | package config
47 |
48 | var encryptCallback func(plainText string) (cipherText string)
49 | var decryptCallback func(cipherText string) (secret string)
50 |
51 | func Initialize(
52 | encryptHandler func(plainText string) (cipherText string),
53 | decryptHandler func(cipherText string) (secret string),
54 | */
55 |
--------------------------------------------------------------------------------
/internal/http/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/http/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/http/http.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | import "net/http"
7 |
8 | func UrlExists(url string) (exists bool) {
9 | trace("http.Head to %q", url)
10 | resp, err := http.Head(url)
11 | if err != nil {
12 | trace("http.Head to %q failed with %v", url, err)
13 | return false
14 | }
15 | if resp.StatusCode != 200 {
16 | trace("http.Head to %q returned status code %d", url, resp.StatusCode)
17 | return false
18 | }
19 |
20 | trace("http.Head to %q succeeded", url)
21 |
22 | return true
23 | }
24 |
--------------------------------------------------------------------------------
/internal/http/http_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "net/http"
9 | "net/http/httptest"
10 | "testing"
11 | )
12 |
13 | func TestUrlExists(t *testing.T) {
14 | // Test case 1: URL exists and returns a 200 status code
15 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
16 | w.WriteHeader(http.StatusOK)
17 | }))
18 | defer ts.Close()
19 | assert.True(t, UrlExists(ts.URL), "Expected UrlExists to return true for URL %q, but got false", ts.URL)
20 |
21 | // Test case 2: URL exists but returns a non-200 status code
22 | ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23 | w.WriteHeader(http.StatusNotFound)
24 | }))
25 | defer ts.Close()
26 | assert.False(t, UrlExists(ts.URL), "Expected UrlExists to return false for URL %q, but got true", ts.URL)
27 |
28 | assert.False(t, UrlExists("http://invalid.url"), "Expected UrlExists to return false for invalid URL, but got true")
29 | }
30 |
--------------------------------------------------------------------------------
/internal/http/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | func init() {
7 | Initialize(
8 | func(err error) {
9 | if err != nil {
10 | panic(err)
11 | }
12 | },
13 | func(format string, a ...any) {})
14 | }
15 |
16 | func Initialize(
17 | errorHandler func(err error),
18 | traceHandler func(format string, a ...any)) {
19 |
20 | errorCallback = errorHandler
21 | traceCallback = traceHandler
22 | }
23 |
--------------------------------------------------------------------------------
/internal/http/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package http
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/intialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package internal
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/config"
8 | "github.com/microsoft/go-sqlcmd/internal/container"
9 | "github.com/microsoft/go-sqlcmd/internal/http"
10 | "github.com/microsoft/go-sqlcmd/internal/io/file"
11 | "github.com/microsoft/go-sqlcmd/internal/net"
12 | "github.com/microsoft/go-sqlcmd/internal/pal"
13 | "github.com/microsoft/go-sqlcmd/internal/secret"
14 | "github.com/microsoft/go-sqlcmd/internal/sql"
15 | )
16 |
17 | type InitializeOptions struct {
18 | ErrorHandler func(error)
19 | TraceHandler func(format string, a ...any)
20 | HintHandler func([]string)
21 | LineBreak string
22 | }
23 |
24 | // Initialize initializes various dependencies for the application with the provided options.
25 | // The dependencies that are initialized include file, sql, config, container,
26 | // secret, net, and pal. This function is typically called at the start of the application
27 | // to ensure that all dependencies are properly initialized before any other code is executed.
28 | func Initialize(options InitializeOptions) {
29 | if options.ErrorHandler == nil {
30 | panic("ErrorHandler is nil")
31 | }
32 | if options.TraceHandler == nil {
33 | panic("TraceHandler is nil")
34 | }
35 | if options.HintHandler == nil {
36 | panic("HintHandler is nil")
37 | }
38 | if options.LineBreak == "" {
39 | panic("LineBreak is empty")
40 | }
41 | file.Initialize(options.ErrorHandler, options.TraceHandler)
42 | sql.Initialize(options.ErrorHandler, options.TraceHandler, secret.Decode)
43 | config.Initialize(options.ErrorHandler, options.TraceHandler, secret.Encode, secret.Decode, net.IsLocalPortAvailable)
44 | container.Initialize(options.ErrorHandler, options.TraceHandler)
45 | secret.Initialize(options.ErrorHandler)
46 | net.Initialize(options.ErrorHandler, options.TraceHandler)
47 | http.Initialize(options.ErrorHandler, options.TraceHandler)
48 | pal.Initialize(options.ErrorHandler, options.LineBreak)
49 | }
50 |
--------------------------------------------------------------------------------
/internal/intialize_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package internal
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/output"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestInitialize(t *testing.T) {
13 | o := output.New(output.Options{HintHandler: func(hints []string) {
14 | }, ErrorHandler: func(err error) {}})
15 | options := InitializeOptions{
16 | ErrorHandler: func(err error) {
17 | if err != nil {
18 | panic(err)
19 | }
20 | },
21 | HintHandler: func(strings []string) {},
22 | TraceHandler: o.Tracef,
23 | LineBreak: "\n",
24 | }
25 | Initialize(options)
26 | }
27 |
28 | func TestNegInitialize(t *testing.T) {
29 | options := InitializeOptions{
30 | ErrorHandler: nil,
31 | }
32 | assert.Panics(t, func() {
33 | Initialize(options)
34 | })
35 | }
36 |
37 | func TestNegInitialize2(t *testing.T) {
38 | options := InitializeOptions{
39 | ErrorHandler: func(err error) {},
40 | }
41 | assert.Panics(t, func() {
42 | Initialize(options)
43 | })
44 | }
45 |
46 | func TestNegInitialize3(t *testing.T) {
47 | options := InitializeOptions{
48 | ErrorHandler: func(err error) {},
49 | TraceHandler: func(format string, a ...any) {},
50 | }
51 | assert.Panics(t, func() {
52 | Initialize(options)
53 | })
54 | }
55 |
56 | func TestNegInitialize4(t *testing.T) {
57 | options := InitializeOptions{
58 | ErrorHandler: func(err error) {},
59 | TraceHandler: func(format string, a ...any) {},
60 | HintHandler: func(strings []string) {},
61 | }
62 | assert.Panics(t, func() {
63 | Initialize(options)
64 | })
65 | }
66 |
67 | func TestNegInitialize5(t *testing.T) {
68 | options := InitializeOptions{
69 | ErrorHandler: func(err error) {},
70 | TraceHandler: func(format string, a ...any) {},
71 | HintHandler: func(strings []string) {},
72 | LineBreak: "",
73 | }
74 | assert.Panics(t, func() {
75 | Initialize(options)
76 | })
77 | }
78 |
--------------------------------------------------------------------------------
/internal/io/file/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package file
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/io/file/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package file
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/io/file/file.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package file
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/io/folder"
8 | "io/ioutil"
9 | "os"
10 | "path/filepath"
11 | )
12 |
13 | func CloseFile(f *os.File) {
14 | err := f.Close()
15 | checkErr(err)
16 | }
17 |
18 | // CreateEmptyIfNotExists creates an empty file with the given filename if it
19 | // does not already exist. If the parent directory of the file does not exist, the
20 | // function will create it. The function is useful for ensuring that a file is
21 | // present before writing to it.
22 | func CreateEmptyIfNotExists(filename string) {
23 | if filename == "" {
24 | panic("filename must not be empty")
25 | }
26 |
27 | d, _ := filepath.Split(filename)
28 | if d != "" && !Exists(d) {
29 | trace("Folder %v does not exist, creating", d)
30 | folder.MkdirAll(d)
31 | }
32 | if !Exists(filename) {
33 | trace("File %v does not exist, creating empty 0 byte file", filename)
34 | handle, err := os.Create(filename)
35 | checkErr(err)
36 | defer func() {
37 | err := handle.Close()
38 | checkErr(err)
39 | }()
40 | }
41 | }
42 |
43 | // Exists checks if a file with the given filename exists in the file system. It
44 | // returns a boolean value indicating whether the file exists or not.
45 | func Exists(filename string) (exists bool) {
46 | if filename == "" {
47 | panic("filename must not be empty")
48 | }
49 |
50 | if _, err := os.Stat(filename); err == nil {
51 | exists = true
52 | }
53 |
54 | return
55 | }
56 |
57 | func GetContents(filename string) string {
58 | b, err := ioutil.ReadFile(filename)
59 | checkErr(err)
60 |
61 | return string(b)
62 | }
63 |
64 | func OpenFile(filename string) *os.File {
65 | f, err := os.Create(filename)
66 | checkErr(err)
67 | return f
68 | }
69 |
70 | // Remove is used to remove a file with the specified filename. The function
71 | // takes in the name of the file as an argument and deletes it from the file system.
72 | func Remove(filename string) {
73 | err := os.Remove(filename)
74 | checkErr(err)
75 | }
76 |
77 | func WriteString(f *os.File, s string) {
78 | _, err := f.WriteString(s)
79 | checkErr(err)
80 | }
81 |
--------------------------------------------------------------------------------
/internal/io/file/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package file
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/io/folder"
8 | )
9 |
10 | func init() {
11 | Initialize(
12 | func(err error) {
13 | if err != nil {
14 | panic(err)
15 | }
16 | },
17 | func(format string, a ...any) {})
18 | }
19 |
20 | func Initialize(
21 | errorHandler func(err error),
22 | traceHandler func(format string, a ...any)) {
23 | errorCallback = errorHandler
24 | traceCallback = traceHandler
25 |
26 | // this file helper depends on the folder helper (for example, to create folder paths
27 | // in passed in file names if the folders don't exist
28 | folder.Initialize(errorHandler, traceHandler)
29 | }
30 |
--------------------------------------------------------------------------------
/internal/io/file/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package file
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/io/folder/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/io/folder/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/io/folder/folder.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | import (
7 | "os"
8 | )
9 |
10 | // Getwd returns the current working directory
11 | func Getwd() string {
12 | path, err := os.Getwd()
13 | checkErr(err)
14 | return path
15 | }
16 |
17 | // MkdirAll creates a directory with the given name if it does not already exist.
18 | func MkdirAll(folder string) {
19 | if folder == "" {
20 | panic("folder must not be empty")
21 | }
22 | if _, err := os.Stat(folder); os.IsNotExist(err) {
23 | trace("Folder %v does not exist, creating", folder)
24 | err := os.MkdirAll(folder, os.ModePerm)
25 | checkErr(err)
26 | }
27 | }
28 |
29 | // RemoveAll removes a folder and all of its contents at the given path.
30 | func RemoveAll(folder string) {
31 | err := os.RemoveAll(folder)
32 | checkErr(err)
33 | }
34 |
--------------------------------------------------------------------------------
/internal/io/folder/folder_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "os"
9 | "strings"
10 | "testing"
11 | )
12 |
13 | func TestMkdirAll(t *testing.T) {
14 | folderName := "test-folder"
15 | type args struct {
16 | folder string
17 | }
18 | tests := []struct {
19 | name string
20 | args args
21 | }{
22 | {name: "default", args: args{folder: folderName}},
23 | {name: "noFolderNamePanic", args: args{folder: ""}},
24 | }
25 |
26 | cleanup(folderName)
27 | defer cleanup(folderName)
28 |
29 | for _, tt := range tests {
30 | t.Run(tt.name, func(t *testing.T) {
31 |
32 | // If test name ends in 'Panic' expect a Panic
33 | if strings.HasSuffix(tt.name, "Panic") {
34 | assert.Panics(t, func() {
35 | MkdirAll(tt.args.folder)
36 | })
37 | } else {
38 | MkdirAll(tt.args.folder)
39 | }
40 |
41 | })
42 | }
43 | }
44 |
45 | func TestGetwd(t *testing.T) {
46 | // Test 1: Check that the function returns the correct path
47 | wd, err := os.Getwd()
48 | assert.NoErrorf(t, err, "unexpected error: %v", err)
49 | assert.Equal(t, Getwd(), wd)
50 | }
51 |
52 | func cleanup(folderName string) {
53 | RemoveAll(folderName)
54 | }
55 |
--------------------------------------------------------------------------------
/internal/io/folder/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | func init() {
7 | Initialize(
8 | func(err error) {
9 | if err != nil {
10 | panic(err)
11 | }
12 | },
13 | func(format string, a ...any) {})
14 | }
15 |
16 | func Initialize(
17 | errorHandler func(err error),
18 | traceHandler func(format string, a ...any)) {
19 |
20 | errorCallback = errorHandler
21 | traceCallback = traceHandler
22 | }
23 |
--------------------------------------------------------------------------------
/internal/io/folder/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package folder
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/localizer/constants.go:
--------------------------------------------------------------------------------
1 | package localizer
2 |
3 | const (
4 | UseContextCommand = "sqlcmd config use-context mssql"
5 | PasswordEnvVar = "SQLCMD_PASSWORD"
6 | PasswordEnvVar2 = "SQLCMDPASSWORD"
7 | EndpointFlag = "--endpoint"
8 | FeedbackUrl = "https://aka.ms/sqlcmd-feedback"
9 | PasswordEncryptFlag = "--password-encryption"
10 | AuthTypeFlag = "--auth-type"
11 | ModernAuthTypeBasic = "basic"
12 | ModernAuthTypeOther = "other"
13 | UserNameFlag = "--username"
14 | NameFlag = "--name"
15 | GetContextCommand = "sqlcmd config get-contexts"
16 | GetEndpointsCommand = "sqlcmd config get-endpoints"
17 | GetUsersCommand = "sqlcmd config get-users"
18 | RunQueryExample = "sqlcmd query \"SELECT @@SERVERNAME\""
19 | UninstallCommand = "sqlcmd uninstall"
20 | AcceptEulaFlag = "--accept-eula"
21 | AcceptEulaEnvVar = "SQLCMD_ACCEPT_EULA"
22 | PodmanPsCommand = "podman ps"
23 | DockerPsCommand = "docker ps"
24 | HelpFlag = "--help"
25 | QueryAndExitFlag = "-Q"
26 | QueryFlag = "-q"
27 | DbNameVar = "SQLCMDDBNAME"
28 | BatchTerminatorGo = "GO"
29 | ConnStrPattern = "[[tcp:]|[lpc:]|[np:]]server[\\instance_name][,port]"
30 | ServerEnvVar = "SQLCMDSERVER"
31 | InsertKeyword = "INSERT"
32 | PacketSizeVar = "SQLCMDPACKETSIZE"
33 | LoginTimeOutVar = "SQLCMDLOGINTIMEOUT"
34 | WorkstationVar = "SQLCMDWORKSTATION"
35 | ApplicationIntentFlagShort = "-K"
36 | DosErrorLevel = "DOS ERRORLEVEL"
37 | StdoutName = "stdout"
38 | ColSeparatorVar = "SQLCMDCOLSEP"
39 | ErrorLevel = "ERRORLEVEL"
40 | AppIntentValues = `"readonly"`
41 | FormatValues = "\"horiz\",\"horizontal\",\"vert\",\"vertical\""
42 | ErrToStderrValues = "\"-1\",\"0\",\"1\""
43 | )
44 |
--------------------------------------------------------------------------------
/internal/localizer/localizer.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package localizer
5 |
6 | import (
7 | // Import the internal/translations so that its init() function
8 | // is run. It's really important that we do this here so that the
9 | // default message catalog is updated to use our translations
10 | // *before* we initialize the message.Printer instances below.
11 |
12 | "fmt"
13 | "os"
14 | "strings"
15 |
16 | _ "github.com/microsoft/go-sqlcmd/internal/translations"
17 | "golang.org/x/text/language"
18 | "golang.org/x/text/message"
19 | )
20 |
21 | var Translator *message.Printer
22 |
23 | var supportedLanguages = map[string]string{
24 | "de-de": "de-DE",
25 | "fr-fr": "fr-FR",
26 | "en-us": "en-US",
27 | "zh-hans": "zh-CN",
28 | "zh-cn": "zh-CN",
29 | "zh-hant": "zh-TW",
30 | "zh-tw": "zh-TW",
31 | "it-it": "it-IT",
32 | "ja-jp": "ja-JP",
33 | "ko-kr": "ko-KR",
34 | "pt-br": "pt-BR",
35 | "ru-ru": "ru-RU",
36 | "es-es": "es-ES",
37 | }
38 |
39 | // init() initializes the language automatically
40 | // based on env var SQLCMD_LANG which expects language
41 | // tag such as en-us, de-de, fr-ch, etc.
42 | func init() {
43 | localeName := strings.ToLower(os.Getenv("SQLCMD_LANG"))
44 | if _, ok := supportedLanguages[localeName]; !ok {
45 | localeName = "en-us"
46 | }
47 | Translator = message.NewPrinter(language.MustParse(supportedLanguages[localeName]))
48 | }
49 |
50 | // Errorf() is wrapper function to create localized errors
51 | func Errorf(format string, a ...any) error {
52 | errMsg := Translator.Sprintf(format, a...)
53 | return fmt.Errorf(errMsg)
54 | }
55 |
56 | // Sprintf() is wrapper function to create localized string
57 | func Sprintf(key message.Reference, args ...interface{}) string {
58 | return Translator.Sprintf(key, args...)
59 | }
60 |
61 | // ProductBanner() returns the localized product banner string
62 | func ProductBanner() string {
63 | return Sprintf("sqlcmd: Install/Create/Query SQL Server, Azure SQL, and Tools")
64 | }
65 |
--------------------------------------------------------------------------------
/internal/localizer/localizer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package localizer
5 |
6 | import (
7 | "os"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/assert"
11 | )
12 |
13 | // TestErrorfMissingLanguage tests that Errorf returns the correct error message when the language is not found
14 | func TestErrorfMissingLanguage(t *testing.T) {
15 | os.Setenv("SQLCMD_LANG", "xx-xx")
16 | err := Errorf("This is a %s error", "test")
17 | assert.EqualError(t, err, "This is a test error")
18 | }
19 |
20 | // TestSprintfPanic tests that Sprintf panics when the language is not found
21 | func TestSprintfPanic(t *testing.T) {
22 | os.Setenv("SQLCMD_LANG", "")
23 | assert.Panics(t, func() { Sprintf(nil, "Missing key") })
24 | }
25 |
--------------------------------------------------------------------------------
/internal/net/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/net/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/net/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | func init() {
7 | Initialize(
8 | func(err error) {
9 | if err != nil {
10 | panic(err)
11 | }
12 | },
13 | func(format string, a ...any) {})
14 | }
15 |
16 | func Initialize(
17 | errorHandler func(err error),
18 | traceHandler func(format string, a ...any)) {
19 |
20 | errorCallback = errorHandler
21 | traceCallback = traceHandler
22 | }
23 |
--------------------------------------------------------------------------------
/internal/net/net.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | import (
7 | "net"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | // IsLocalPortAvailable takes a port number and returns a boolean indicating
13 | // whether the port is available for use.
14 | func IsLocalPortAvailable(port int) (portAvailable bool) {
15 | timeout := time.Second
16 |
17 | hostPort := net.JoinHostPort("localhost", strconv.Itoa(port))
18 | trace(
19 | "Checking if local port %#v is available using DialTimeout(tcp, %v, timeout: %d)",
20 | port,
21 | hostPort,
22 | timeout,
23 | )
24 | conn, err := net.DialTimeout(
25 | "tcp",
26 | hostPort,
27 | timeout,
28 | )
29 | if err != nil {
30 | trace(
31 | "Expected connecting error '%v' on local port %#v, therefore port is available)",
32 | err,
33 | port,
34 | )
35 | portAvailable = true
36 | }
37 | if conn != nil {
38 | err := conn.Close()
39 | checkErr(err)
40 | trace("Local port '%#v' is not available", port)
41 | } else {
42 | trace("Local port '%#v' is available", port)
43 | }
44 |
45 | return
46 | }
47 |
--------------------------------------------------------------------------------
/internal/net/net_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | import (
7 | "testing"
8 | )
9 |
10 | // TestIsLocalPortAvailable verified the function for both available and unavailable
11 | // code (this function expects a local SQL Server instance listening on port 1433
12 | func TestIsLocalPortAvailable(t *testing.T) {
13 | var testedPortAvailable bool
14 | var testedNotPortAvailable bool
15 |
16 | for i := 1432; i <= 1434; i++ {
17 | isPortAvailable := IsLocalPortAvailable(i)
18 | if isPortAvailable {
19 | testedPortAvailable = true
20 | t.Logf("Port %#v is available", i)
21 | } else {
22 | testedNotPortAvailable = true
23 | t.Logf("Port %#v is not available", i)
24 | }
25 | if testedPortAvailable && testedNotPortAvailable {
26 | return
27 | }
28 | }
29 |
30 | t.Log("Didn't find both an available port and unavailable port")
31 | t.Fail()
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/internal/net/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package net
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/output/factory.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package output
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/internal/output/formatter"
9 | "github.com/microsoft/go-sqlcmd/internal/test"
10 | "os"
11 | )
12 |
13 | // New initializes a new Output instance with the specified options. If options
14 | // are not provided, default values are used. The function sets the error callback
15 | // and the hint callback based on the value of the unitTesting field in the
16 | // provided options. If unitTesting is true, the error callback is set to
17 | // panic on error, otherwise it is set to use cobra.CheckErr to handle errors.
18 | func New(options Options) *Output {
19 | if options.LoggingLevel == 0 {
20 | options.LoggingLevel = 2
21 | }
22 | if options.StandardWriter == nil {
23 | options.StandardWriter = os.Stdout
24 | }
25 | if options.ErrorHandler == nil {
26 | if test.IsRunningInTestExecutor() && !options.unitTesting {
27 | options.ErrorHandler = func(err error) {
28 | if err != nil {
29 | panic(err)
30 | }
31 | }
32 | } else {
33 | panic("Must provide Error Handler (the process (" + os.Args[0] + ") host is not a test executor)")
34 | }
35 |
36 | }
37 | if options.HintHandler == nil {
38 | if test.IsRunningInTestExecutor() && !options.unitTesting {
39 | options.HintHandler = func(hints []string) {
40 | fmt.Println(hints)
41 | }
42 | } else {
43 | panic("Must provide hint handler (the process " + os.Args[0] + " host is not a test executor)")
44 | }
45 | }
46 |
47 | f := formatter.New(formatter.Options{
48 | SerializationFormat: options.OutputType,
49 | StandardOutput: options.StandardWriter,
50 | ErrorHandler: options.ErrorHandler,
51 | })
52 |
53 | return &Output{
54 | formatter: f,
55 | loggingLevel: options.LoggingLevel,
56 | standardWriteCloser: options.StandardWriter,
57 | errorCallback: options.ErrorHandler,
58 | hintCallback: options.HintHandler,
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/internal/output/factory_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package output
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func TestFactory(t *testing.T) {
12 | o := New(Options{unitTesting: false, HintHandler: func(hints []string) {
13 |
14 | }, ErrorHandler: func(err error) {
15 |
16 | }})
17 | o.errorCallback(nil)
18 | }
19 |
20 | func TestNegtactory(t *testing.T) {
21 | assert.Panics(t, func() {
22 | New(Options{unitTesting: true,
23 | HintHandler: func(hints []string) {},
24 | ErrorHandler: nil})
25 | })
26 | }
27 |
28 | func TestNegFactory2(t *testing.T) {
29 | assert.Panics(t, func() {
30 | New(Options{unitTesting: true,
31 | HintHandler: nil,
32 | ErrorHandler: func(err error) {}})
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/internal/output/formatter/base.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import "io"
7 |
8 | type Base struct {
9 | StandardOutput io.WriteCloser
10 | ErrorHandlerCallback func(err error)
11 | }
12 |
13 | func (f *Base) CheckErr(err error) {
14 | if f.ErrorHandlerCallback == nil {
15 | panic("errorHandlerCallback not initialized")
16 | }
17 |
18 | f.ErrorHandlerCallback(err)
19 | }
20 |
21 | func (f *Base) Output(bytes []byte) {
22 | _, err := f.StandardOutput.Write(bytes)
23 | f.CheckErr(err)
24 | }
25 |
--------------------------------------------------------------------------------
/internal/output/formatter/base_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func TestBase_CheckErr(t *testing.T) {
12 | f := &Base{ErrorHandlerCallback: nil}
13 |
14 | assert.Panics(t, func() {
15 | f.CheckErr(nil)
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/internal/output/formatter/factory.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | )
7 |
8 | // New creates a new instance of the Formatter interface. It takes an Options
9 | // struct as input and sets default values for some of the fields if they are
10 | // not specified. The SerializationFormat field of the Options struct is used
11 | // to determine which implementation of the Formatter interface to return.
12 | // If the specified format is not supported, the function will panic.
13 | func New(options Options,
14 | ) (f Formatter) {
15 | if options.SerializationFormat == "" {
16 | options.SerializationFormat = "yaml"
17 | }
18 | if options.ErrorHandler == nil {
19 | options.ErrorHandler = func(err error) {}
20 | }
21 | if options.StandardOutput == nil {
22 | options.StandardOutput = os.Stdout
23 | }
24 |
25 | switch options.SerializationFormat {
26 | case "json":
27 | f = &Json{Base: Base{
28 | StandardOutput: options.StandardOutput,
29 | ErrorHandlerCallback: options.ErrorHandler}}
30 | case "yaml":
31 | f = &Yaml{Base: Base{
32 | StandardOutput: options.StandardOutput,
33 | ErrorHandlerCallback: options.ErrorHandler}}
34 | case "xml":
35 | f = &Xml{Base: Base{
36 | StandardOutput: options.StandardOutput,
37 | ErrorHandlerCallback: options.ErrorHandler}}
38 | default:
39 | panic(fmt.Sprintf("Format '%v' not supported", options.SerializationFormat))
40 | }
41 |
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/internal/output/formatter/factory_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "log"
9 | "testing"
10 | )
11 |
12 | func TestFormatter(t *testing.T) {
13 | s := []string{"serialize this"}
14 |
15 | var f Formatter
16 | f = New(Options{SerializationFormat: "yaml"})
17 | f.Serialize(s)
18 | f = New(Options{SerializationFormat: "xml"})
19 | f.Serialize(s)
20 | f = New(Options{SerializationFormat: "json"})
21 | f.Serialize(s)
22 |
23 | log.Println("This is here to ensure a newline is in test output")
24 | }
25 |
26 | func TestNegFormatterBadFormat(t *testing.T) {
27 | assert.Panics(t, func() {
28 | New(Options{SerializationFormat: "badbad"})
29 | })
30 | }
31 |
32 | func TestFormatterEmptyFormat(t *testing.T) {
33 | s := "serialize this"
34 | f := New(Options{SerializationFormat: ""})
35 | f.Serialize(s)
36 | }
37 |
--------------------------------------------------------------------------------
/internal/output/formatter/interface.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | // Formatter defines a formatter for serializing an input object into a byte slice.
7 | // The Serialize method serializes the input object and returns the resulting
8 | // byte slice. The CheckErr method handles any error encountered during
9 | // the serialization process.
10 | type Formatter interface {
11 | Serialize(in interface{}) (bytes []byte)
12 | CheckErr(err error)
13 | }
14 |
--------------------------------------------------------------------------------
/internal/output/formatter/json.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import (
7 | "encoding/json"
8 | )
9 |
10 | type Json struct {
11 | Base
12 | }
13 |
14 | func (f *Json) Serialize(in interface{}) (bytes []byte) {
15 | var err error
16 |
17 | bytes, err = json.MarshalIndent(in, "", " ")
18 | f.Base.CheckErr(err)
19 | f.Base.Output(bytes)
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/internal/output/formatter/options.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import "io"
4 |
5 | // Options defines the options for creating a new Formatter instance.
6 | type Options struct {
7 | SerializationFormat string
8 | StandardOutput io.WriteCloser
9 | ErrorHandler func(err error)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/output/formatter/xml.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import (
7 | "encoding/xml"
8 | )
9 |
10 | type Xml struct {
11 | Base
12 | }
13 |
14 | func (f *Xml) Serialize(in interface{}) (bytes []byte) {
15 | var err error
16 |
17 | bytes, err = xml.MarshalIndent(in, "", " ")
18 | f.Base.CheckErr(err)
19 | f.Base.Output(bytes)
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/internal/output/formatter/yaml.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package formatter
5 |
6 | import (
7 | "gopkg.in/yaml.v2"
8 | )
9 |
10 | type Yaml struct {
11 | Base
12 | }
13 |
14 | func (f *Yaml) Serialize(in interface{}) (bytes []byte) {
15 | var err error
16 |
17 | bytes, err = yaml.Marshal(in)
18 | f.Base.CheckErr(err)
19 | f.Base.Output(bytes)
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/internal/output/options.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "github.com/microsoft/go-sqlcmd/internal/output/verbosity"
5 | "io"
6 | )
7 |
8 | type Options struct {
9 | OutputType string
10 | LoggingLevel verbosity.Level
11 | StandardWriter io.WriteCloser
12 |
13 | ErrorHandler func(err error)
14 | HintHandler func(hints []string)
15 |
16 | unitTesting bool
17 | }
18 |
--------------------------------------------------------------------------------
/internal/output/type.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package output
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/output/formatter"
8 | "github.com/microsoft/go-sqlcmd/internal/output/verbosity"
9 | "io"
10 | )
11 |
12 | type Output struct {
13 | errorCallback func(err error)
14 | hintCallback func(hints []string)
15 |
16 | formatter formatter.Formatter
17 | loggingLevel verbosity.Level
18 | standardWriteCloser io.WriteCloser
19 | }
20 |
--------------------------------------------------------------------------------
/internal/output/verbosity/level.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package verbosity
5 |
6 | // Level is an enumeration representing different verbosity levels for logging,
7 | // ranging from Error to Trace. The values of the enumeration are
8 | // Error, Warn, Info, Debug, and Trace, in increasing order of verbosity.
9 | type Level int
10 |
11 | const (
12 | Error Level = iota
13 | Warn
14 | Info
15 | Debug
16 | Trace
17 | )
18 |
--------------------------------------------------------------------------------
/internal/pal/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/pal/intialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | func init() {
7 | Initialize(func(err error) {
8 | if err != nil {
9 | panic(err)
10 | }
11 | }, defaultLineBreak())
12 | }
13 |
14 | func Initialize(handler func(err error), endOfLine string) {
15 | errorCallback = handler
16 | lineBreak = endOfLine
17 | }
18 |
--------------------------------------------------------------------------------
/internal/pal/pal.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | // Package pal provides functions that need to operate differently across different
5 | // operating systems and/or platforms.
6 | package pal
7 |
8 | import (
9 | "os"
10 | "path/filepath"
11 | "strings"
12 | )
13 |
14 | var lineBreak string
15 |
16 | // FilenameInUserHomeDotDirectory returns the full path and filename
17 | // to the filename in the dotDirectory (e.g. .sqlcmd) in the user's home directory
18 | // e.g. c:\users\username
19 | func FilenameInUserHomeDotDirectory(dotDirectory string, filename string) string {
20 | home, err := os.UserHomeDir()
21 | checkErr(err)
22 |
23 | return filepath.Join(home, dotDirectory, filename)
24 | }
25 |
26 | // UserName returns the name of the currently logged-in user
27 | func UserName() (userName string) {
28 | return username()
29 | }
30 |
31 | // CmdLineWithEnvVars generates a command-line that can be run at the
32 | // operating system command-line (e.g. bash or cmd) which also requires
33 | // one or more environment variables to also be set
34 | func CmdLineWithEnvVars(vars []string, cmd string) string {
35 | var sb strings.Builder
36 | for _, v := range vars {
37 | sb.WriteString(CreateEnvVarKeyword())
38 | sb.WriteString(" " + cliQuoteIdentifier() + v + cliQuoteIdentifier())
39 | }
40 | sb.WriteString(cliCommandSeparator())
41 | sb.WriteString(cmd)
42 |
43 | return sb.String()
44 | }
45 |
46 | func LineBreak() string {
47 | if lineBreak == "" {
48 | panic("Initialize has not been called")
49 | }
50 |
51 | return lineBreak
52 | }
53 |
--------------------------------------------------------------------------------
/internal/pal/pal_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | import "os"
7 |
8 | func CreateEnvVarKeyword() string {
9 | return "export"
10 | }
11 |
12 | func cliQuoteIdentifier() string {
13 | return `'`
14 | }
15 |
16 | func cliCommandSeparator() string {
17 | return `; `
18 | }
19 |
20 | func defaultLineBreak() string {
21 | return "\r\n"
22 | }
23 |
24 | func username() string {
25 | return os.Getenv("USER")
26 | }
27 |
--------------------------------------------------------------------------------
/internal/pal/pal_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | import "os"
7 |
8 | func CreateEnvVarKeyword() string {
9 | return "export"
10 | }
11 |
12 | func cliQuoteIdentifier() string {
13 | return `'`
14 | }
15 |
16 | func cliCommandSeparator() string {
17 | return `; `
18 | }
19 |
20 | func defaultLineBreak() string {
21 | return "\n"
22 | }
23 |
24 | func username() string {
25 | return os.Getenv("USER")
26 | }
27 |
--------------------------------------------------------------------------------
/internal/pal/pal_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | import (
7 | "errors"
8 | "fmt"
9 | "github.com/stretchr/testify/assert"
10 | "testing"
11 | )
12 |
13 | func TestFilenameInUserHomeDotDirectory(t *testing.T) {
14 | FilenameInUserHomeDotDirectory(".foo", "bar")
15 | }
16 |
17 | func TestLineBreak(t *testing.T) {
18 | LineBreak()
19 | }
20 |
21 | func TestNegLineBreak(t *testing.T) {
22 | lineBreak = ""
23 | assert.Panics(t, func() {
24 | LineBreak()
25 | })
26 | }
27 |
28 | func TestCheckErr(t *testing.T) {
29 | assert.Panics(t, func() {
30 | checkErr(errors.New("test"))
31 | })
32 | }
33 |
34 | func TestUserName(t *testing.T) {
35 | user := UserName()
36 | fmt.Println(user)
37 | }
38 |
39 | func TestCmdLineWithEnvVars(t *testing.T) {
40 | cmdLine := CmdLineWithEnvVars([]string{"ENVVAR=FOOBAR"}, "cmd-to-run.exe")
41 | fmt.Println(cmdLine)
42 | }
43 |
--------------------------------------------------------------------------------
/internal/pal/pal_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package pal
5 |
6 | import "os"
7 |
8 | func CreateEnvVarKeyword() string {
9 | return "SET"
10 | }
11 |
12 | func cliQuoteIdentifier() string {
13 | return `"`
14 | }
15 |
16 | func cliCommandSeparator() string {
17 | return ` & `
18 | }
19 |
20 | func defaultLineBreak() string {
21 | return "\n"
22 | }
23 |
24 | func username() string {
25 | return os.Getenv("USERNAME")
26 | }
27 |
--------------------------------------------------------------------------------
/internal/secret/encryption_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | var encryptionMethods = []string{"none"}
7 |
8 | func encrypt(plainText string) (cipherText string) {
9 |
10 | //BUG(stuartpa): Encryption not yet implemented on macOS, will use the KeyChain
11 | cipherText = plainText
12 |
13 | return
14 | }
15 |
16 | func decrypt(cipherText string) (secret string) {
17 | secret = cipherText
18 |
19 | //BUG(stuartpa): Encryption not yet implemented on macOS, will use the KeyChain
20 | return
21 | }
22 |
--------------------------------------------------------------------------------
/internal/secret/encryption_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | var encryptionMethods = []string{"none"}
7 |
8 | func encrypt(plainText string) (cipherText string) {
9 |
10 | //BUG(stuartpa): Encryption not yet implemented on linux
11 | cipherText = plainText
12 |
13 | return
14 | }
15 |
16 | func decrypt(cipherText string) (secret string) {
17 | secret = cipherText
18 |
19 | //BUG(stuartpa): Encryption not yet implemented on linux
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/internal/secret/encryption_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | import (
7 | "github.com/billgraziano/dpapi"
8 | )
9 |
10 | var encryptionMethods = []string{"none", "dpapi"}
11 |
12 | func encrypt(plainText string) (cipherText string) {
13 | var err error
14 |
15 | cipherText, err = dpapi.Encrypt(plainText)
16 | checkErr(err)
17 |
18 | return
19 | }
20 |
21 | func decrypt(cipherText string) (secret string) {
22 | var err error
23 |
24 | secret, err = dpapi.Decrypt(cipherText)
25 | checkErr(err)
26 |
27 | return
28 | }
29 |
--------------------------------------------------------------------------------
/internal/secret/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/secret/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/secret/generate.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | import (
7 | "crypto/rand"
8 | "math/big"
9 | mathRand "math/rand"
10 | "strings"
11 | )
12 |
13 | const (
14 | lowerCharSet = "abcdedfghijklmnopqrstuvwxyz"
15 | upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
16 | numberSet = "0123456789"
17 | )
18 |
19 | // Generate generates a random password of a specified length. The password
20 | // will contain at least the specified number of special characters,
21 | // numeric digits, and upper-case letters. The remaining characters in the
22 | // password will be selected from a combination of lower-case letters, special
23 | // characters, and numeric digits. The special characters are chosen from
24 | // the provided special character set. The generated password is returned
25 | // as a string.
26 | func Generate(passwordLength, minSpecialChar, minNum, minUpperCase int, specialCharSet string) string {
27 | var password strings.Builder
28 | allCharSet := lowerCharSet + upperCharSet + specialCharSet + numberSet
29 |
30 | //Set special character
31 | for i := 0; i < minSpecialChar; i++ {
32 | idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(specialCharSet))))
33 | checkErr(err)
34 | _, err = password.WriteString(string(specialCharSet[idx.Int64()]))
35 | checkErr(err)
36 | }
37 |
38 | //Set numeric
39 | for i := 0; i < minNum; i++ {
40 | idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(numberSet))))
41 | checkErr(err)
42 | _, err = password.WriteString(string(numberSet[idx.Int64()]))
43 | checkErr(err)
44 | }
45 |
46 | //Set uppercase
47 | for i := 0; i < minUpperCase; i++ {
48 | idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(upperCharSet))))
49 | checkErr(err)
50 | _, err = password.WriteString(string(upperCharSet[idx.Int64()]))
51 | checkErr(err)
52 | }
53 |
54 | remainingLength := passwordLength - minSpecialChar - minNum - minUpperCase
55 | for i := 0; i < remainingLength; i++ {
56 | idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(allCharSet))))
57 | checkErr(err)
58 | _, err = password.WriteString(string(allCharSet[idx.Int64()]))
59 | checkErr(err)
60 | }
61 |
62 | inRune := []rune(password.String())
63 | mathRand.Shuffle(len(inRune), func(i, j int) {
64 | inRune[i], inRune[j] = inRune[j], inRune[i]
65 | })
66 | return string(inRune)
67 | }
68 |
--------------------------------------------------------------------------------
/internal/secret/generate_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func TestGenerate(t *testing.T) {
12 | type args struct {
13 | passwordLength int
14 | minSpecialChar int
15 | minNum int
16 | minUpperCase int
17 | specialChars string
18 | }
19 | tests := []struct {
20 | name string
21 | args args
22 | want string
23 | }{
24 | {
25 | name: "positiveSimple",
26 | args: args{
27 | passwordLength: 50,
28 | minSpecialChar: 10,
29 | minNum: 10,
30 | minUpperCase: 10,
31 | specialChars: "!@#$%&*",
32 | },
33 | want: "",
34 | },
35 | }
36 | for _, tt := range tests {
37 | t.Run(tt.name, func(t *testing.T) {
38 | got := Generate(
39 | tt.args.passwordLength,
40 | tt.args.minSpecialChar,
41 | tt.args.minNum,
42 | tt.args.minUpperCase,
43 | tt.args.specialChars,
44 | )
45 | assert.Len(t, got, tt.args.passwordLength)
46 | })
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/internal/secret/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | func init() {
7 | Initialize(func(err error) {
8 | if err != nil {
9 | panic(err)
10 | }
11 | })
12 | }
13 |
14 | func Initialize(handler func(err error)) {
15 | errorCallback = handler
16 | }
17 |
--------------------------------------------------------------------------------
/internal/secret/secret_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package secret
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "runtime"
9 | "testing"
10 | )
11 |
12 | func TestEncodeAndDecode(t *testing.T) {
13 | notEncrypted := Encode("plainText", "none")
14 | plainText := Decode(notEncrypted, "none")
15 | assert.Equal(t, "plainText", plainText)
16 |
17 | if runtime.GOOS == "windows" {
18 | encrypted := Encode("plainText", "dpapi")
19 | plainText := Decode(encrypted, "dpapi")
20 | assert.Equal(t, "plainText", plainText)
21 | }
22 | }
23 |
24 | func TestNegEncode(t *testing.T) {
25 | assert.Panics(t, func() {
26 | Encode("", "none")
27 | })
28 | }
29 |
30 | func TestNegEncodeBadEncryptionMethod(t *testing.T) {
31 | assert.Panics(t, func() {
32 | Encode("plainText", "does-not-exist")
33 | })
34 | }
35 |
36 | func TestNegDecode(t *testing.T) {
37 | assert.Panics(t, func() {
38 | Decode("", "none")
39 | })
40 | }
41 |
42 | func TestNegDecodeBadEncryptionMethod(t *testing.T) {
43 | assert.Panics(t, func() {
44 | Decode("cipherText", "does-not-exist")
45 | })
46 | }
47 |
48 | func TestDecodeAsUtf16(t *testing.T) {
49 | cipherText := Encode("plainText", "none")
50 | plainText := DecodeAsUtf16(cipherText, "none")
51 | assert.Equal(t, []byte{0x70, 0x0, 0x6c, 0x0, 0x61, 0x0, 0x69, 0x0, 0x6e, 0x0, 0x54, 0x0, 0x65, 0x0, 0x78, 0x0, 0x74, 0x0}, plainText)
52 | }
53 |
54 | // TestEncryptionMethodsForUsage ensures at least "none" is an available
55 | // encryption method for usage.
56 | func TestEncryptionMethodsForUsage(t *testing.T) {
57 | s := EncryptionMethodsForUsage()
58 | assert.Contains(t, s, "none")
59 | }
60 |
61 | func TestIsValidEncryptionMethod(t *testing.T) {
62 | assert.True(t, IsValidEncryptionMethod("none"))
63 | assert.False(t, IsValidEncryptionMethod("does-not-exist"))
64 | }
65 |
--------------------------------------------------------------------------------
/internal/sql/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | var errorCallback func(err error)
7 |
8 | func checkErr(err error) {
9 | errorCallback(err)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/sql/error_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | import (
7 | "errors"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func Test_checkErr(t *testing.T) {
13 | assert.Panics(t, func() {
14 | checkErr(errors.New("verify error handler"))
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/internal/sql/factory.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | type SqlOptions struct {
7 | UnitTesting bool
8 | }
9 |
10 | func New(options SqlOptions) Sql {
11 | if options.UnitTesting {
12 | return &mock{}
13 | } else {
14 | return &mssql{}
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/internal/sql/initialize.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | var decryptCallback func(cipherText string, encryptionMethod string) (secret string)
7 |
8 | func Initialize(
9 | errorHandler func(err error),
10 | traceHandler func(format string, a ...any),
11 | decryptHandler func(cipherText string, encryptionMethod string) (secret string)) {
12 | errorCallback = errorHandler
13 | traceCallback = traceHandler
14 | decryptCallback = decryptHandler
15 | }
16 |
--------------------------------------------------------------------------------
/internal/sql/interface.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | import (
7 | . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 | )
9 |
10 | type Sql interface {
11 | Connect(endpoint Endpoint, user *User, options ConnectOptions)
12 | Query(text string)
13 | ScalarString(query string) string
14 | }
15 |
16 | type ConnectOptions struct {
17 | Database string
18 |
19 | Interactive bool
20 | }
21 |
--------------------------------------------------------------------------------
/internal/sql/mock.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | import . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
7 |
8 | // Connect is a mock implementation used to speed up unit testing of other units
9 | func (m *mock) Connect(
10 | endpoint Endpoint,
11 | user *User,
12 | options ConnectOptions,
13 | ) {
14 | }
15 |
16 | // Query is a mock implementation used to speed up unit testing of other units
17 | func (m *mock) Query(text string) {
18 | }
19 |
20 | func (m *mock) ScalarString(query string) string {
21 | return ""
22 | }
23 |
--------------------------------------------------------------------------------
/internal/sql/mock_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | import "testing"
7 | import . "github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
8 |
9 | func TestMockConnect(t *testing.T) {
10 | mockObj := mock{}
11 | mockObj.Connect(Endpoint{}, nil, ConnectOptions{})
12 | }
13 |
14 | func TestMockQuery(t *testing.T) {
15 | mockObj := mock{}
16 | mockObj.Query("")
17 | }
18 |
--------------------------------------------------------------------------------
/internal/sql/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | var traceCallback func(format string, a ...any)
7 |
8 | func trace(format string, a ...any) {
9 | traceCallback(format, a...)
10 | }
11 |
--------------------------------------------------------------------------------
/internal/sql/types.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sql
5 |
6 | import "github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
7 |
8 | // mssql implements for SQL Server
9 | type mssql struct {
10 | sqlcmd *sqlcmd.Sqlcmd
11 | console sqlcmd.Console
12 | }
13 |
14 | // mock impoements for unit testing which uses a Hello World container (no
15 | // SQL)
16 | type mock struct{}
17 |
--------------------------------------------------------------------------------
/internal/test/executor.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package test
5 |
6 | import (
7 | "os"
8 | "strings"
9 | )
10 |
11 | func IsRunningInTestExecutor() bool {
12 | if strings.HasSuffix(os.Args[0], ".test") || // are we in go test on *nix?
13 | strings.HasSuffix(os.Args[0], ".test.exe") || // are we in go test on windows?
14 | (len(os.Args) > 1 && os.Args[1] == "-test.v") { // are we in goland unittest?
15 | return true
16 | } else {
17 | return false
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/test/executor_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package test
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "os"
9 | "testing"
10 | )
11 |
12 | func TestIsRunningInTestExecutor(t *testing.T) {
13 | // Test for running in go test on *nix
14 | os.Args[0] = "main.test"
15 | assert.True(t, IsRunningInTestExecutor(), "Failed to detect running in go test on *nix")
16 |
17 | // Test for running in go test on windows
18 | os.Args[0] = "main.test.exe"
19 | assert.True(t, IsRunningInTestExecutor(), "Failed to detect running in go test on windows")
20 |
21 | // Test for running in goland unittest
22 | os.Args = []string{"main", "-test.v"}
23 | assert.True(t, IsRunningInTestExecutor(), "Failed to detect running in goland unittest")
24 |
25 | // Test for not running in test executor
26 | os.Args = []string{"main"}
27 | assert.False(t, IsRunningInTestExecutor(), "Incorrectly detected running in test executor")
28 |
29 | // Test for invalid arguments
30 | os.Args = []string{"main", "-invalid"}
31 | assert.False(t, IsRunningInTestExecutor(), "Incorrectly detected running in test executor")
32 | }
33 |
--------------------------------------------------------------------------------
/internal/tools/factory.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tools
5 |
6 | import (
7 | "fmt"
8 | "github.com/microsoft/go-sqlcmd/internal/tools/tool"
9 | )
10 |
11 | var calledBefore bool
12 |
13 | func NewTool(toolName string) (tool tool.Tool) {
14 | if !calledBefore {
15 | // Init all the tools
16 | for _, t := range tools {
17 | t.Init()
18 | }
19 | calledBefore = true
20 | }
21 |
22 | // Return the asked for tool
23 | for _, t := range tools {
24 | if t.Name() == toolName {
25 | tool = t
26 | }
27 | }
28 |
29 | if tool == nil {
30 | panic(fmt.Sprintf("Tool %q is not a supported tool", toolName))
31 | }
32 |
33 | return tool
34 | }
35 |
--------------------------------------------------------------------------------
/internal/tools/factory_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tools
5 |
6 | import (
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 |
11 | "github.com/microsoft/go-sqlcmd/internal/tools/tool"
12 | )
13 |
14 | type TestTool struct {
15 | tool.Tool
16 | name string
17 | }
18 |
19 | func (t *TestTool) Name() string {
20 | return t.name
21 | }
22 |
23 | func (t *TestTool) Init() {}
24 |
25 | func TestNewTool(t *testing.T) {
26 | // Create some test tools
27 | tool1 := &TestTool{name: "tool1"}
28 | tool2 := &TestTool{name: "tool2"}
29 |
30 | // Set up the list of tools
31 | tools = []tool.Tool{tool1, tool2}
32 |
33 | // Test that we get back the right tool
34 | result := NewTool("tool1")
35 | assert.Equal(t, result, tool1, "Expected tool1 but got %v", result)
36 |
37 | // Test that we get an error for an unsupported tool
38 | expectedErr := fmt.Sprintf("Tool %q is not a supported tool", "unsupported")
39 | defer func() {
40 | if r := recover(); r == nil {
41 | t.Errorf("Expected panic but didn't get one")
42 | } else if r != expectedErr {
43 | t.Errorf("Expected panic message %q but got %q", expectedErr, r)
44 | }
45 | }()
46 |
47 | _ = NewTool("unsupported")
48 | }
49 |
--------------------------------------------------------------------------------
/internal/tools/tool/ads.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/io/file"
8 | "github.com/microsoft/go-sqlcmd/internal/test"
9 | )
10 |
11 | type AzureDataStudio struct {
12 | tool
13 | }
14 |
15 | func (t *AzureDataStudio) Init() {
16 | t.tool.SetToolDescription(Description{
17 | Name: "ads",
18 | Purpose: "Azure Data Studio is a database tool for data professionals who use on-premises and cloud data platforms.",
19 | InstallText: t.installText()})
20 |
21 | for _, location := range t.searchLocations() {
22 | if file.Exists(location) {
23 | t.tool.SetExePathAndName(location)
24 | break
25 | }
26 | }
27 | }
28 |
29 | func (t *AzureDataStudio) Run(args []string) (int, error) {
30 | if !test.IsRunningInTestExecutor() {
31 | return t.tool.Run(args)
32 | } else {
33 | return 0, nil
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/internal/tools/tool/ads_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "os"
8 | "path/filepath"
9 | )
10 |
11 | func (t *AzureDataStudio) searchLocations() []string {
12 | userProfile := os.Getenv("HOME")
13 |
14 | return []string{
15 | filepath.Join("/", "Applications", "Azure Data Studio - Insiders.app"),
16 | filepath.Join(userProfile, "Downloads", "Azure Data Studio - Insiders.app"),
17 | filepath.Join("/", "Applications", "Azure Data Studio.app"),
18 | filepath.Join(userProfile, "Downloads", "Azure Data Studio.app"),
19 | }
20 | }
21 |
22 | func (t *AzureDataStudio) installText() string {
23 | return `Download the latest .zip from:
24 |
25 | https://go.microsoft.com/fwlink/?linkid=2151311
26 |
27 | More information can be found here:
28 |
29 | https://docs.microsoft.com/sql/azure-data-studio/download-azure-data-studio?#get-azure-data-studio-for-macos`
30 | }
31 |
--------------------------------------------------------------------------------
/internal/tools/tool/ads_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | func (t *AzureDataStudio) searchLocations() []string {
7 | panic("Not yet implemented")
8 | }
9 |
10 | func (t *AzureDataStudio) installText() string {
11 | return `Follow the instructions here:
12 |
13 | https://docs.microsoft.com/sql/azure-data-studio/download-azure-data-studio?#get-azure-data-studio-for-linux`
14 | }
15 |
--------------------------------------------------------------------------------
/internal/tools/tool/ads_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "github.com/stretchr/testify/assert"
8 | "path/filepath"
9 | "runtime"
10 | "testing"
11 | )
12 |
13 | func TestAzureDataStudio_Init(t *testing.T) {
14 | if runtime.GOOS == "linux" {
15 | t.Skip("Not yet implemented on Linux")
16 | }
17 | t.Parallel()
18 |
19 | t.Run("found", func(t *testing.T) {
20 | t.Parallel()
21 |
22 | ads := &AzureDataStudio{}
23 | ads.Init()
24 |
25 | filepath.Base(ads.exeName)
26 | })
27 |
28 | }
29 |
30 | func TestAzureDataStudio_Run(t *testing.T) {
31 | if runtime.GOOS == "linux" {
32 | t.Skip("Not yet implemented on Linux")
33 | }
34 | t.Parallel()
35 |
36 | ads := &AzureDataStudio{}
37 | ads.Init()
38 | ads.IsInstalled()
39 | _, _ = ads.Run(nil)
40 | }
41 |
42 | func TestAzureDataStudio_searchLocations(t *testing.T) {
43 | if runtime.GOOS == "linux" {
44 | t.Skip("Not yet implemented on Linux")
45 | }
46 | t.Parallel()
47 |
48 | got := (&AzureDataStudio{}).searchLocations()
49 |
50 | assert.GreaterOrEqual(t, len(got), 1, "expecting 1 or search locations for Azure Data Studio on Windows, got %d", len(got))
51 | }
52 |
53 | func TestAzureDataStudio_installText(t *testing.T) {
54 | if runtime.GOOS == "linux" {
55 | t.Skip("Not yet implemented on Linux")
56 | }
57 |
58 | t.Parallel()
59 |
60 | got := (&AzureDataStudio{}).installText()
61 |
62 | assert.GreaterOrEqual(t, len(got), 1, "no install text provided")
63 | }
64 |
--------------------------------------------------------------------------------
/internal/tools/tool/ads_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "os"
8 | "path/filepath"
9 | )
10 |
11 | // Search in this order
12 | //
13 | // User Insiders Install
14 | // System Insiders Install
15 | // User non-Insiders install
16 | // System non-Insiders install
17 | func (t *AzureDataStudio) searchLocations() []string {
18 | userProfile := os.Getenv("USERPROFILE")
19 | programFiles := os.Getenv("ProgramFiles")
20 |
21 | return []string{
22 | filepath.Join(userProfile, "AppData\\Local\\Programs\\Azure Data Studio - Insiders\\azuredatastudio-insiders.exe"),
23 | filepath.Join(programFiles, "Azure Data Studio - Insiders\\azuredatastudio-insiders.exe"),
24 | filepath.Join(userProfile, "AppData\\Local\\Programs\\Azure Data Studio\\azuredatastudio.exe"),
25 | filepath.Join(programFiles, "Azure Data Studio\\azuredatastudio.exe"),
26 | }
27 | }
28 |
29 | func (t *AzureDataStudio) installText() string {
30 | return `Download the latest 'User Installer' .msi from:
31 |
32 | https://go.microsoft.com/fwlink/?linkid=2150927
33 |
34 | More information can be found here:
35 |
36 | https://docs.microsoft.com/sql/azure-data-studio/download-azure-data-studio#get-azure-data-studio-for-windows`
37 | }
38 |
--------------------------------------------------------------------------------
/internal/tools/tool/interface.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | type Tool interface {
7 | Init()
8 | Name() (name string)
9 | Run(args []string) (exitCode int, err error)
10 | IsInstalled() bool
11 | HowToInstall() string
12 | }
13 |
--------------------------------------------------------------------------------
/internal/tools/tool/tool.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "fmt"
8 | "strings"
9 |
10 | "github.com/microsoft/go-sqlcmd/internal/io/file"
11 | )
12 |
13 | func (t *tool) Init() {
14 | panic("Do not call directly")
15 | }
16 |
17 | func (t *tool) Name() string {
18 | return t.description.Name
19 | }
20 |
21 | func (t *tool) SetExePathAndName(exeName string) {
22 | t.exeName = exeName
23 | }
24 |
25 | func (t *tool) SetToolDescription(description Description) {
26 | t.description = description
27 | }
28 |
29 | func (t *tool) IsInstalled() bool {
30 | if t.installed != nil {
31 | return *t.installed
32 | }
33 |
34 | t.installed = new(bool)
35 | if file.Exists(t.exeName) {
36 | *t.installed = true
37 | } else {
38 | *t.installed = false
39 | }
40 |
41 | return *t.installed
42 | }
43 |
44 | func (t *tool) HowToInstall() string {
45 | var sb strings.Builder
46 |
47 | sb.WriteString("\n\n")
48 | sb.WriteString(fmt.Sprintf("%q is not installed on this machine.\n\n", t.description.Name))
49 | sb.WriteString(fmt.Sprintf("%v\n\n", t.description.Purpose))
50 | sb.WriteString(fmt.Sprintf("To install %q...\n\n%v\n", t.description.Name, t.description.InstallText))
51 |
52 | return sb.String()
53 | }
54 |
55 | func (t *tool) Run(args []string) (int, error) {
56 | if t.installed == nil {
57 | panic("Call IsInstalled before Run")
58 | }
59 |
60 | cmd := t.generateCommandLine(args)
61 | err := cmd.Run()
62 |
63 | return cmd.ProcessState.ExitCode(), err
64 | }
65 |
--------------------------------------------------------------------------------
/internal/tools/tool/tool_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "bytes"
8 | "os/exec"
9 | )
10 |
11 | func (t *tool) generateCommandLine(args []string) *exec.Cmd {
12 | path, _ := exec.LookPath("open")
13 |
14 | args = append([]string{"--args"}, args...)
15 | args = append([]string{t.exeName}, args...)
16 | args = append([]string{"-a"}, args...)
17 |
18 | var stdout, stderr bytes.Buffer
19 | cmd := &exec.Cmd{
20 | Path: path,
21 | Args: args,
22 | Stdout: &stdout,
23 | Stderr: &stderr,
24 | }
25 | return cmd
26 | }
27 |
--------------------------------------------------------------------------------
/internal/tools/tool/tool_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "os/exec"
8 | )
9 |
10 | func (t *tool) generateCommandLine(args []string) *exec.Cmd {
11 | panic("Not yet implemented")
12 | }
13 |
--------------------------------------------------------------------------------
/internal/tools/tool/tool_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | import (
7 | "bytes"
8 | "os/exec"
9 | )
10 |
11 | func (t *tool) generateCommandLine(args []string) *exec.Cmd {
12 | var stdout, stderr bytes.Buffer
13 | cmd := &exec.Cmd{
14 | Path: t.exeName,
15 | Args: args,
16 | Stdout: &stdout,
17 | Stderr: &stderr,
18 | }
19 | return cmd
20 | }
21 |
--------------------------------------------------------------------------------
/internal/tools/tool/types.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tool
5 |
6 | type tool struct {
7 | installed *bool
8 | lookPathError error
9 | exeName string
10 | description Description
11 | }
12 |
13 | type Description struct {
14 | // Name of the tool, e.g. "Azure Data Studio"
15 | Name string
16 |
17 | // Purpose describes what this tool does
18 | Purpose string
19 |
20 | // How to install the tool, e.g. what URL to go to, to download it
21 | InstallText string
22 | }
23 |
--------------------------------------------------------------------------------
/internal/tools/tools.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package tools
5 |
6 | import (
7 | "github.com/microsoft/go-sqlcmd/internal/tools/tool"
8 | )
9 |
10 | var tools = []tool.Tool{
11 | &tool.AzureDataStudio{},
12 | }
13 |
--------------------------------------------------------------------------------
/internal/translations/LocProject.json:
--------------------------------------------------------------------------------
1 | {
2 | "Projects": [
3 | {
4 | "LanguageSet": "AzureKatal_Languages",
5 | "LocItems": [
6 | {
7 | "SourceFile": "internal\\translations\\locales\\en-US\\out.gotext.json",
8 | "CopyOption": "LangIDOnPath",
9 | "Languages": "de-DE;fr-FR;it-IT;es-ES;ja-JP;ko-KR,zh-CN,zh-TW,pt-BR,ru-RU",
10 | "LssFiles": ["internal\\translations\\P306PairNamesToProcess.lss"],
11 | "LclFile": "internal\\translations\\LCL\\{Lang}\\out.gotext.json.lcl",
12 | "OutputPath": "internal\\translations\\localized"
13 | }
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/internal/translations/P306PairNamesToProcess.lss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/internal/translations/catalog_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package translations
5 |
6 | import (
7 | "testing"
8 |
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | // TestLookup tests the Lookup method of the dictionary type.
13 | func TestLookup(t *testing.T) {
14 | d := &dictionary{
15 | index: []uint32{0, 10, 11, 12, 13, 14, 15},
16 | data: "abcdefghijklmnopqrstuvwxyz",
17 | }
18 |
19 | testCases := []struct {
20 | name string
21 | key string
22 | expected string
23 | ok bool
24 | }{
25 | {
26 | name: "non-existing key",
27 | key: "non-existing key",
28 | expected: "",
29 | ok: false,
30 | },
31 | {
32 | name: "existing key",
33 | key: "View configuration information and connection strings",
34 | expected: "k",
35 | ok: true,
36 | },
37 | }
38 |
39 | for _, tc := range testCases {
40 | t.Run(tc.name, func(t *testing.T) {
41 | actual, ok := d.Lookup(tc.key)
42 | assert.Equal(t, tc.expected, actual)
43 | assert.Equal(t, tc.ok, ok)
44 | })
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/internal/translations/translations.go:
--------------------------------------------------------------------------------
1 | package translations
2 |
3 | //go:generate gotext -srclang=en-US update -out=catalog.go -lang=en-US,de-DE,fr-FR,zh-CN,zh-TW,it-IT,ja-JP,ko-KR,pt-BR,ru-RU,es-ES github.com/microsoft/go-sqlcmd/cmd/modern github.com/microsoft/go-sqlcmd/cmd/sqlcmd github.com/microsoft/go-sqlcmd/pkg/sqlcmd
4 |
--------------------------------------------------------------------------------
/pkg/console/console_redirect.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package console
5 |
6 | import (
7 | "os"
8 | "golang.org/x/term"
9 | )
10 |
11 | // isStdinRedirected checks if stdin is coming from a pipe or redirection
12 | func isStdinRedirected() bool {
13 | stat, err := os.Stdin.Stat()
14 | if err != nil {
15 | // If we can't determine, assume it's not redirected
16 | return false
17 | }
18 |
19 | // If it's not a character device, it's coming from a pipe or redirection
20 | if (stat.Mode() & os.ModeCharDevice) == 0 {
21 | return true
22 | }
23 |
24 | // Double-check using term.IsTerminal
25 | fd := int(os.Stdin.Fd())
26 | return !term.IsTerminal(fd)
27 | }
--------------------------------------------------------------------------------
/pkg/console/console_redirect_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package console
5 |
6 | import (
7 | "io"
8 | "os"
9 | "testing"
10 | )
11 |
12 | func TestStdinRedirectionDetection(t *testing.T) {
13 | // Save original stdin
14 | origStdin := os.Stdin
15 | defer func() { os.Stdin = origStdin }()
16 |
17 | // Create a pipe
18 | r, w, err := os.Pipe()
19 | if err != nil {
20 | t.Fatalf("Couldn't create pipe: %v", err)
21 | }
22 | defer r.Close()
23 | defer w.Close()
24 |
25 | // Replace stdin with our pipe
26 | os.Stdin = r
27 |
28 | // Test if stdin is properly detected as redirected
29 | if !isStdinRedirected() {
30 | t.Errorf("Pipe input should be detected as redirected")
31 | }
32 |
33 | // Write some test input
34 | go func() {
35 | _, _ = io.WriteString(w, "test input\n")
36 | w.Close()
37 | }()
38 |
39 | // Create console with redirected stdin
40 | console := NewConsole("")
41 |
42 | // Test readline
43 | line, err := console.Readline()
44 | if err != nil {
45 | t.Fatalf("Failed to read from redirected stdin: %v", err)
46 | }
47 |
48 | if line != "test input" {
49 | t.Errorf("Expected 'test input', got '%s'", line)
50 | }
51 |
52 | // Clean up
53 | console.Close()
54 | }
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/imports_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmdlinter
5 |
6 | import (
7 | "os"
8 | "path/filepath"
9 | "testing"
10 |
11 | "golang.org/x/tools/go/analysis/analysistest"
12 | )
13 |
14 | func TestImports(t *testing.T) {
15 | wd, err := os.Getwd()
16 | if err != nil {
17 | t.Errorf("Failed to get wd: %s", err)
18 | }
19 | analysistest.Run(t, filepath.Join(wd, `testdata`), ImportsAnalyzer, "imports_linter_tests/...")
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/github.com/README.md:
--------------------------------------------------------------------------------
1 | This directory structure exists to provide stub package implementations for the linter tests. The `analysistest` package replaces `$GOPATH` with the local file system path. We create these stubs so the linter test files can closely mimic our production code.
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/github.com/alecthomas/chroma/chroma.go:
--------------------------------------------------------------------------------
1 | package chroma
2 |
3 | var C = 1
4 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/github.com/microsoft/go-sqlcmd/pkg/sqlcmd/sqlcmd.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | var S = 1
4 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/github.com/stretchr/testify/assert/main.go:
--------------------------------------------------------------------------------
1 | package assert
2 |
3 | import "testing"
4 |
5 | func NotNil(t *testing.T, object interface{}, msgAndArgs ...interface{}) bool {
6 | return false
7 | }
8 |
9 | func NoError(t *testing.T, err error, msgAndArgs ...interface{}) bool {
10 | return false
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/imports_linter_tests/cmd/main/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | import (
7 | _ "github.com/alecthomas/chroma" // want "Non-internal packages should not import \"github.com/alecthomas/chroma\""
8 | _ "github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
9 | _ "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var X = 1
13 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/imports_linter_tests/internal/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package main
5 |
6 | import (
7 | _ "fmt"
8 |
9 | _ "github.com/microsoft/go-sqlcmd/pkg/sqlcmd" // want "Internal packages should not import \"github.com/microsoft/go-sqlcmd/pkg/sqlcmd\""
10 | )
11 |
12 | var X = 1
13 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/testdata/src/useassert_linter_tests/assert.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestDontUseFatal(t *testing.T) {
11 | t.Fatal("this should fail") // want "Use assert package methods instead of Fatal"
12 | t.Fatalf("this should %s", "fail") // want "Use assert package methods instead of Fatalf"
13 | t.Fail() // want "Use assert package methods instead of Fail"
14 | assert.NoError(t, fmt.Errorf("what"))
15 | t.FailNow() // want "Use assert package methods instead of FailNow"
16 |
17 | }
18 |
19 | func TestDontUseRecover(t *testing.T) {
20 | defer func() { assert.NotNil(t, recover(), "The code did not panic as expected") }() // want "Use assert.Panics instead of recover()"
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/useasserts.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmdlinter
5 |
6 | import (
7 | "go/ast"
8 |
9 | "golang.org/x/tools/go/analysis"
10 | "golang.org/x/tools/go/analysis/passes/inspect"
11 | "golang.org/x/tools/go/ast/inspector"
12 | )
13 |
14 | var AssertAnalyzer = &analysis.Analyzer{
15 | Name: "assertlint",
16 | Doc: "Require use of asserts instead of fmt.Error functions in tests",
17 | Requires: []*analysis.Analyzer{inspect.Analyzer},
18 | Run: runAsserts,
19 | }
20 |
21 | var blockedTestingMethods = []string{"Error", "ErrorF", "Fail", "FailNow", "Fatal", "Fatalf"}
22 |
23 | func runAsserts(pass *analysis.Pass) (interface{}, error) {
24 | // pass.ResultOf[inspect.Analyzer] will be set if we've added inspect.Analyzer to Requires.
25 | // Analyze code and make an AST from the file:
26 | inspectorInstance := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
27 |
28 | nodeFilter := []ast.Node{(*ast.CallExpr)(nil)}
29 |
30 | inspectorInstance.Preorder(nodeFilter, func(n ast.Node) {
31 | node := n.(*ast.CallExpr)
32 | switch fun := node.Fun.(type) {
33 | case (*ast.SelectorExpr):
34 | switch funX := fun.X.(type) {
35 | case (*ast.Ident):
36 | if funX.Name == "t" && contains(blockedTestingMethods, fun.Sel.Name) {
37 | pass.Reportf(node.Pos(), "Use assert package methods instead of %s", fun.Sel.Name)
38 | }
39 | default:
40 | return
41 | }
42 | case (*ast.Ident):
43 | if fun.Name == "recover" {
44 | pass.Reportf(node.Pos(), "Use assert.Panics instead of recover()")
45 | }
46 | }
47 | })
48 | return nil, nil
49 | }
50 |
51 | func contains(a []string, v string) bool {
52 | for _, val := range a {
53 | if val == v {
54 | return true
55 | }
56 | }
57 | return false
58 | }
59 |
--------------------------------------------------------------------------------
/pkg/sqlcmd-linter/useasserts_test.go:
--------------------------------------------------------------------------------
1 | package sqlcmdlinter
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 |
8 | "golang.org/x/tools/go/analysis/analysistest"
9 | )
10 |
11 | func TestUseAsserts(t *testing.T) {
12 | wd, err := os.Getwd()
13 | if err != nil {
14 | t.Errorf("Failed to get wd: %s", err)
15 | }
16 | analysistest.Run(t, filepath.Join(wd, `testdata`), AssertAnalyzer, "useassert_linter_tests/...")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/azure_auth.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | import (
7 | "database/sql/driver"
8 | "fmt"
9 | "net/url"
10 | "os"
11 |
12 | "github.com/microsoft/go-mssqldb/azuread"
13 | )
14 |
15 | const (
16 | NotSpecified = "NotSpecified"
17 | SqlPassword = "SqlPassword"
18 | sqlClientId = "a94f9c62-97fe-4d19-b06d-472bed8d2bcf"
19 | )
20 |
21 | func getSqlClientId() string {
22 | if clientId := os.Getenv("SQLCMDCLIENTID"); clientId != "" {
23 | return clientId
24 | }
25 | return sqlClientId
26 | }
27 |
28 | func GetTokenBasedConnection(connstr string, authenticationMethod string) (driver.Connector, error) {
29 |
30 | connectionUrl, err := url.Parse(connstr)
31 | if err != nil {
32 | return nil, err
33 | }
34 |
35 | query := connectionUrl.Query()
36 | query.Set("fedauth", authenticationMethod)
37 | query.Set("applicationclientid", getSqlClientId())
38 | switch authenticationMethod {
39 | case azuread.ActiveDirectoryServicePrincipal, azuread.ActiveDirectoryApplication:
40 | query.Set("clientcertpath", os.Getenv("AZURE_CLIENT_CERTIFICATE_PATH"))
41 | case azuread.ActiveDirectoryInteractive:
42 | case azuread.ActiveDirectoryDeviceCode:
43 | loginTimeout := query.Get("connection timeout")
44 | loginTimeoutSeconds := 0
45 | if loginTimeout != "" {
46 | _, _ = fmt.Sscanf(loginTimeout, "%d", &loginTimeoutSeconds)
47 | }
48 | // AAD interactive needs minutes at minimum
49 | if loginTimeoutSeconds > 0 && loginTimeoutSeconds < 120 {
50 | query.Set("connection timeout", "120")
51 | }
52 | }
53 |
54 | connectionUrl.RawQuery = query.Encode()
55 | return azuread.NewConnector(connectionUrl.String())
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/exec_darwin.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | import (
4 | "os/exec"
5 | )
6 |
7 | func sysCommand(arg string) *exec.Cmd {
8 | cmd := exec.Command(comSpec(), "-c", arg)
9 | return cmd
10 | }
11 |
12 | // comSpec returns the path of the command shell executable
13 | func comSpec() string {
14 | // /bin/sh will be a link to the shell
15 | return `/bin/sh`
16 | }
17 |
18 | const defaultEditor = "vi"
19 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/exec_linux.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | import (
4 | "os/exec"
5 | )
6 |
7 | func sysCommand(arg string) *exec.Cmd {
8 | cmd := exec.Command(comSpec(), "-c", arg)
9 | return cmd
10 | }
11 |
12 | // comSpec returns the path of the command shell executable
13 | func comSpec() string {
14 | // /bin/sh will be a link to the shell
15 | return `/bin/sh`
16 | }
17 |
18 | const defaultEditor = "vi"
19 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/exec_windows.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | import (
4 | "os"
5 | "os/exec"
6 | "syscall"
7 | )
8 |
9 | func sysCommand(arg string) *exec.Cmd {
10 | cmd := exec.Command(comSpec())
11 | cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: cmd.Path + " " + comArgs(arg)}
12 | return cmd
13 | }
14 |
15 | // comSpec returns the path of the command shell executable
16 | func comSpec() string {
17 | if cmd, ok := os.LookupEnv("COMSPEC"); ok {
18 | return cmd
19 | }
20 | return `C:\Windows\System32\cmd.exe`
21 | }
22 |
23 | func comArgs(args string) string {
24 | return `/c ` + args
25 | }
26 |
27 | const defaultEditor = "notepad.exe"
28 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/format_darwin.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | // SqlcmdEol is the end-of-line marker for sqlcmd output
7 | const SqlcmdEol = "\n"
8 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/format_linux.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | // SqlcmdEol is the end-of-line marker for sqlcmd output
7 | const SqlcmdEol = "\n"
8 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/format_windows.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | // SqlcmdEol is the end-of-line marker for sqlcmd output
7 | const SqlcmdEol = "\r\n"
8 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/parse.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
6 | import (
7 | "strings"
8 | "unicode"
9 | )
10 |
11 | // grab grabs i from r, or returns 0 if i >= end.
12 | func grab(r []rune, i, end int) rune {
13 | if i < end {
14 | return r[i]
15 | }
16 | return 0
17 | }
18 |
19 | // readMultilineComment finds the end of a multiline comment (ie, '*/').
20 | func readMultilineComment(r []rune, i, end int) (int, bool) {
21 | i++
22 | for ; i < end; i++ {
23 | if r[i-1] == '*' && r[i] == '/' {
24 | return i, true
25 | }
26 | }
27 | return end, false
28 | }
29 |
30 | // readCommand reads to the next control character to find
31 | // a command in the string. Command regexes constrain matches
32 | // to the beginning of the string, and all commands consume
33 | // an entire line.
34 | func readCommand(c Commands, r []rune, i, end int) (*Command, []string, int) {
35 | for ; i < end; i++ {
36 | next := grab(r, i, end)
37 | if next == 0 || (unicode.IsControl(next) && next != '\t') {
38 | break
39 | }
40 | }
41 | cmd, args := c.matchCommand(string(r[:i]))
42 | return cmd, args, i
43 | }
44 |
45 | // readVariableReference returns the index of the end of the variable reference or false if it's not a valid identifier
46 | func readVariableReference(r []rune, i int, end int) (int, bool) {
47 | for ; i < end; i++ {
48 | if r[i] == ')' {
49 | return i, true
50 | }
51 | if (r[i] >= 'a' && r[i] <= 'z') || (r[i] >= 'A' && r[i] <= 'Z') || (r[i] >= '0' && r[i] <= '9') || strings.ContainsRune(validVariableRunes, r[i]) {
52 | continue
53 | }
54 | break
55 | }
56 | return 0, false
57 | }
58 |
59 | func max64(a, b int64) int64 {
60 | if a > b {
61 | return a
62 | }
63 | return b
64 | }
65 |
66 | // min returns the minimum of a, b.
67 | func min(a, b int) int {
68 | if a < b {
69 | return a
70 | }
71 | return b
72 | }
73 |
74 | func min64(a, b int64) int64 {
75 | if a < b {
76 | return a
77 | }
78 | return b
79 | }
80 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/parse_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT license.
3 |
4 | package sqlcmd
5 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/sqlcmd_windows_amd64.go:
--------------------------------------------------------------------------------
1 | package sqlcmd
2 |
3 | import (
4 | "github.com/microsoft/go-mssqldb/msdsn"
5 | _ "github.com/microsoft/go-mssqldb/namedpipe"
6 | _ "github.com/microsoft/go-mssqldb/sharedmemory"
7 | )
8 |
9 | // Note: The order of includes above matters for namedpipe and sharedmemory.
10 | // Go tools always sort by name.
11 | // init() swaps shared memory protocol with np so it gets priority when dialing.
12 |
13 | func init() {
14 | if len(msdsn.ProtocolParsers) == 4 {
15 | // reorder the protocol parsers to tcp->admin->lpc->np
16 | // Named pipes/shared memory package doesn't support ARM
17 | // Once there's a fix for https://github.com/natefinch/npipe/issues/34 incorporated into go-mssqldb,
18 | // reorder to lpc->admin->np->tcp
19 | var lpc = msdsn.ProtocolParsers[3]
20 | msdsn.ProtocolParsers[3] = msdsn.ProtocolParsers[2]
21 | msdsn.ProtocolParsers[2] = lpc
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/blanks.sql:
--------------------------------------------------------------------------------
1 | set nocount on
2 | :setvar l line2
3 | select 'line 1
4 |
5 |
6 |
7 | $(l)'
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/quotedidentifiers.sql:
--------------------------------------------------------------------------------
1 | set nocount on
2 | set quoted_identifier on
3 | select [a]]b] = 'ab', "a'b" = 1, [a"b] = 'a"b'
4 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/selectdates.sql:
--------------------------------------------------------------------------------
1 | set nocount on
2 | declare @d1 datetime = '2022-03-05 14:01:02'
3 | declare @d2 datetime2(4) = '2021-1-2 11:06:02.2'
4 | declare @d3 datetimeoffset(6) = '2021-5-5'
5 | declare @d4 smalldatetime = '2019-01-11 13:00:00'
6 | declare @d5 time = '14:01:02'
7 | declare @d6 date = '2011-02-03'
8 |
9 | select @d1, @d2, @d3, @d4, @d5, @d6
10 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/singlebatchnogo.sql:
--------------------------------------------------------------------------------
1 | select 100 as num
2 | select 'string' as title
3 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/testerrorredirection.sql:
--------------------------------------------------------------------------------
1 | select '1'
2 | go
3 | select $(var
4 | go
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/twobatchnoendinggo.sql:
--------------------------------------------------------------------------------
1 | select 100 as num
2 | go
3 | select 'string' as title
4 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/twobatchwithgo.sql:
--------------------------------------------------------------------------------
1 | select 100 as num
2 | GO
3 | select 'string' as title
4 | GO
5 |
--------------------------------------------------------------------------------
/pkg/sqlcmd/testdata/variablesnogo.sql:
--------------------------------------------------------------------------------
1 | set nocount on
2 | :setvar hundred 100
3 |
4 | -- verify fix for https://github.com/microsoft/go-sqlcmd/issues/197
5 |
6 | -- Correctly handle the first line of a batch having a variable after an empty line
7 |
8 | GO
9 |
10 | select $(hundred)
11 |
12 | GO
--------------------------------------------------------------------------------
/release/linux/deb/README.md:
--------------------------------------------------------------------------------
1 | # Debian Packaging Release
2 |
3 | ## Building the Debian package
4 |
5 | Execute the following command from the root directory of this repository:
6 |
7 | ``` bash
8 | ./release/linux/debian/pipeline.sh
9 | ```
10 |
11 | Output will be sent to `./output/debian`
12 |
13 | ## Dev Installation and Verification
14 |
15 | ``` bash
16 | ./release/linux/debian/pipeline-test.sh
17 | ```
18 |
19 | ## Release Install/Update/Uninstall Steps
20 |
21 | > **Note:** Replace `{{HOST}}` and `{{CLI_VERSION}}` with the appropriate values.
22 |
23 | ### Install sqlcmd with apt (Ubuntu or Debian)
24 |
25 | 1. Download and install the signing key:
26 |
27 | ```bash
28 | sudo curl -sL http://{{HOST}}/browse/repo/ubuntu/dpgswdist.v1.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/dpgswdist.v1.asc.gpg > /dev/null
29 | ```
30 |
31 | 2. Add the sqlcmd repository information:
32 |
33 | ```bash
34 | sudo echo "deb [trusted=yes arch=amd64] http://{{HOST}}/browse/repo/ubuntu/sqlcmd mssql main" | tee /etc/apt/sources.list.d/sqlcmd.list
35 | ```
36 |
37 | 3. Update repository information and install sqlcmd:
38 |
39 | ```bash
40 | sudo apt-get update
41 | sudo apt-get install sqlcmd
42 | ```
43 |
44 | 5. Verify installation success:
45 |
46 | ```bash
47 | sqlcmd --help
48 | ```
49 |
50 | ### Update
51 |
52 | 1. Upgrade sqlcmd only:
53 |
54 | ```bash
55 | sudo apt-get update && sudo apt-get install --only-upgrade -y sqlcmd
56 | ```
57 |
58 | ### Uninstall
59 |
60 | 1. Uninstall with apt-get remove:
61 |
62 | ```bash
63 | sudo apt-get remove -y sqlcmd
64 | ```
65 |
66 | 2. Remove the sqlcmd repository information:
67 |
68 | > Note: This step is not needed if you plan on installing sqlcmd in the future
69 |
70 | ```bash
71 | sudo rm /etc/apt/sources.list.d/sqlcmd.list
72 | ```
73 |
74 | 3. Remove the signing key:
75 |
76 | ```bash
77 | sudo rm /etc/apt/trusted.gpg.d/dpgswdist.v1.asc.gpg
78 | ```
79 |
80 | 4. Remove any unneeded dependencies that were installed with sqlcmd:
81 |
82 | ```bash
83 | sudo apt autoremove
84 | ```
85 |
--------------------------------------------------------------------------------
/release/linux/deb/build-pkg.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #------------------------------------------------------------------------------
4 | # Copyright (c) Microsoft Corporation.
5 | # Licensed under the MIT license.
6 | #------------------------------------------------------------------------------
7 |
8 | # Description:
9 | #
10 | # Build a debian/ubuntu `sqlcmd` package. This script is intended to be ran in a
11 | # container with the respective disto/image laid down.
12 | #
13 | # Usage:
14 | # $ build-pkg.sh
15 |
16 | set -exv
17 |
18 | : "${CLI_VERSION:?CLI_VERSION environment variable not set.}"
19 | : "${CLI_VERSION_REVISION:?CLI_VERSION_REVISION environment variable not set.}"
20 |
21 | WORKDIR=`cd $(dirname $0); cd ../../../; pwd`
22 |
23 | ls -la ${WORKDIR}
24 |
25 | apt-get -y update || exit 1
26 | export DEBIAN_FRONTEND=noninteractive
27 | apt-get install -y \
28 | debhelper \
29 | dpkg-dev \
30 | locales || exit 1
31 |
32 | # Locale
33 | sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
34 | dpkg-reconfigure --frontend=noninteractive locales && \
35 | update-locale LANG=en_US.UTF-8
36 |
37 | export LANG=en_US.UTF-8
38 | export PATH=$PATH
39 |
40 | # Verify
41 | chmod u+x /mnt/workspace/sqlcmd
42 | /mnt/workspace/sqlcmd --help
43 |
44 | mkdir /opt/stage
45 | cp /mnt/workspace/sqlcmd /opt/stage/sqlcmd
46 |
47 | # Create create directory for debian build
48 | mkdir -p ${WORKDIR}/debian
49 | ${WORKDIR}/release/linux/deb/prepare-rules.sh ${WORKDIR}/debian ${WORKDIR}
50 |
51 | cd ${WORKDIR}
52 | dpkg-buildpackage -us -uc
53 |
54 | ls ${WORKDIR} -R
55 |
56 | debPkg=${WORKDIR}/../sqlcmd_${CLI_VERSION}-${CLI_VERSION_REVISION:=1}_all.deb
57 | cp ${debPkg} /mnt/output/
58 |
--------------------------------------------------------------------------------
/release/linux/docker/README.md:
--------------------------------------------------------------------------------
1 | # Docker Release
2 |
3 | ## Building Docker in CI/CD pipeline
4 |
5 | Execute the following command from the root directory of this repository:
6 |
7 | ```bash
8 | ./release/linux/docker/pipeline.sh
9 | ```
10 |
11 | Output will be sent to `./output/docker`
12 |
13 | ## Verify
14 |
15 | ```bash
16 | ./release/linux/docker/pipeline-test.sh
17 | ```
18 |
--------------------------------------------------------------------------------
/release/linux/docker/pipeline-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #------------------------------------------------------------------------------
4 | # Copyright (c) Microsoft Corporation.
5 | # Licensed under the MIT license.
6 | #------------------------------------------------------------------------------
7 |
8 | # Description:
9 | #
10 | # Instructions to be invoked under the build CI pipeline in AzureDevOps.
11 | #
12 | # Kickoff docker image test:
13 | #
14 | # Usage:
15 | #
16 | # $ pipeline-test.sh
17 |
18 | set -exv
19 |
20 | : "${REPO_ROOT_DIR:=`cd $(dirname $0); cd ../../../; pwd`}"
21 |
22 | PACKAGE_VERSION=${CLI_VERSION:=0.0.1}
23 | PACKAGE_VERSION_REVISION=${CLI_VERSION_REVISION:=1}
24 |
25 | BUILD_ARTIFACTSTAGINGDIRECTORY=${BUILD_ARTIFACTSTAGINGDIRECTORY:=${REPO_ROOT_DIR}/output/docker}
26 | IMAGE_NAME=microsoft/sqlcmd${BUILD_BUILDNUMBER:=''}:latest
27 | TAR_FILE=${BUILD_ARTIFACTSTAGINGDIRECTORY}/sqlcmd-docker-${PACKAGE_VERSION}-${PACKAGE_VERSION_REVISION}.tar
28 |
29 | echo "=========================================================="
30 | echo "PACKAGE_VERSION: ${PACKAGE_VERSION}"
31 | echo "PACKAGE_VERSION_REVISION: ${PACKAGE_VERSION_REVISION}"
32 | echo "BUILD_ARTIFACTSTAGINGDIRECTORY: ${BUILD_ARTIFACTSTAGINGDIRECTORY}"
33 | echo "Image name: ${IMAGE_NAME}"
34 | echo "Docker image file: ${TAR_FILE}"
35 | echo "=========================================================="
36 |
37 | docker load < ${TAR_FILE}
38 | docker run ${IMAGE_NAME} sqlcmd --help || exit 1
39 |
--------------------------------------------------------------------------------
/release/linux/rpm/README.md:
--------------------------------------------------------------------------------
1 | RPM Release
2 |
3 | ## Building RPM in CI/CD pipeline
4 |
5 | Execute the following command from the root directory of this repository:
6 |
7 | ``` bash
8 | ./release/linux/rpm/pipeline.sh
9 | ```
10 | Output will be sent to `./output/rpm`
11 |
12 | To test the packages:
13 |
14 | ``` bash
15 | ./release/linux/rpm/pipeline-test.sh
16 | ```
17 |
18 |
--------------------------------------------------------------------------------
/release/linux/rpm/build-rpm.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #------------------------------------------------------------------------------
4 | # Copyright (c) Microsoft Corporation.
5 | # Licensed under the MIT license.
6 | #------------------------------------------------------------------------------
7 |
8 | # Description:
9 | #
10 | # Build a rpm `sqlcmd` package. This script is intended to be run in a
11 | # container with the respective distro/image laid down.
12 | #
13 | # Usage:
14 | # $ build-rpm.sh
15 |
16 | set -exv
17 |
18 | : "${CLI_VERSION:?CLI_VERSION environment variable not set.}"
19 | : "${CLI_VERSION_REVISION:?CLI_VERSION_REVISION environment variable not set.}"
20 |
21 | yum update -y
22 | yum install -y rpm-build
23 |
24 | export LC_ALL=en_US.UTF-8
25 | export REPO_ROOT_DIR=`cd $(dirname $0); cd ../../../; pwd`
26 |
27 | rpmbuild -v -bb --clean ${REPO_ROOT_DIR}/release/linux/rpm/sqlcmd.spec && cp /root/rpmbuild/RPMS/x86_64/* /mnt/output
28 |
--------------------------------------------------------------------------------
/release/linux/rpm/pipeline.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #------------------------------------------------------------------------------
3 | # Copyright (c) Microsoft Corporation.
4 | # Licensed under the MIT license.
5 | #------------------------------------------------------------------------------
6 |
7 | # Description:
8 | #
9 | # Instructions to be invoked under the build CI pipeline in AzureDevOps.
10 | #
11 | # Kickoff rpm package build. The build pipeline can then save it as an
12 | # artifact as it sees fit.
13 | #
14 | # Usage:
15 | #
16 | # foundation images: `centos:centos7|fedora:29`
17 | #
18 | # $ pipeline.sh
19 |
20 | set -exv
21 |
22 | : "${REPO_ROOT_DIR:=`cd $(dirname $0); cd ../../../; pwd`}"
23 |
24 | if [[ "${BUILD_OUTPUT}" != "" ]]; then
25 | cp ${BUILD_OUTPUT}/SqlcmdLinux-amd64/sqlcmd ${REPO_ROOT_DIR}/sqlcmd
26 | fi
27 |
28 | DIST_DIR=${BUILD_STAGINGDIRECTORY:=${REPO_ROOT_DIR}/output/rpm}
29 | DISTRO_BASE_IMAGE=( centos:centos7 fedora:29 )
30 |
31 | CLI_VERSION=${CLI_VERSION:=0.0.1}
32 |
33 | echo "=========================================================="
34 | echo "CLI_VERSION: ${CLI_VERSION}"
35 | echo "CLI_VERSION_REVISION: ${CLI_VERSION_REVISION:=1}"
36 | echo "Distribution Image: ${DISTRO_BASE_IMAGE}"
37 | echo "Output location: ${DIST_DIR}"
38 | echo "=========================================================="
39 |
40 | mkdir -p ${DIST_DIR} || exit 1
41 |
42 | for i in ${!DISTRO_BASE_IMAGE[@]}; do
43 | image=${DISTRO_BASE_IMAGE[$i]}
44 |
45 | echo "=========================================================="
46 | echo "Build rpm on ${image}"
47 | echo "=========================================================="
48 |
49 | docker run --rm \
50 | -v "${REPO_ROOT_DIR}":/mnt/repo \
51 | -v "${DIST_DIR}":/mnt/output \
52 | -v "${PIPELINE_WORKSPACE}":/mnt/workspace \
53 | -e CLI_VERSION=${CLI_VERSION} \
54 | -e CLI_VERSION_REVISION=${CLI_VERSION_REVISION:=1} \
55 | "${image}" \
56 | /mnt/repo/release/linux/rpm/build-rpm.sh
57 | done
58 |
--------------------------------------------------------------------------------
/release/linux/rpm/sqlcmd.spec:
--------------------------------------------------------------------------------
1 | # RPM spec file for sqlcmd
2 | # Definition of macros used - https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros
3 |
4 | # .el7.centos -> .el7
5 | %if 0%{?rhel}
6 | %define dist .el%{?rhel}
7 | %endif
8 |
9 | %define name sqlcmd
10 | %define release 1%{?dist}
11 | %define version %{getenv:CLI_VERSION}
12 | %define repo_path %{getenv:REPO_ROOT_DIR}
13 | %define cli_lib_dir %{_libdir}/sqlcmd
14 |
15 | %undefine _missing_build_ids_terminate_build
16 | %global _missing_build_ids_terminate_build 0
17 |
18 | Summary: MSSQL SQLCMD CLI Tools
19 | License: https://github.com/microsoft/go-sqlcmd/blob/main/LICENSE
20 | Name: %{name}
21 | Version: %{version}
22 | Release: %{release}
23 | Url: https://github.com/microsoft/go-sqlcmd
24 | BuildArch: x86_64
25 |
26 | %description
27 | SQLCMD CLI, a multi-platform command line experience for Microsoft SQL Server and Azure SQL.
28 |
29 | %prep
30 | %install
31 |
32 | # Create executable
33 | mkdir -p %{buildroot}%{_bindir}
34 | cp %{repo_path}/sqlcmd %{buildroot}%{_bindir}
35 |
36 | %files
37 | %attr(0755,root,root) %{_bindir}/sqlcmd
38 |
--------------------------------------------------------------------------------
/release/windows/choco/sqlcmd.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sqlcmd
6 | 0.8.1
7 | sqlcmd (Install)
8 | Microsoft
9 | https://docs.microsoft.com/sql/tools/go-sqlcmd-utility
10 | https://github.com/microsoft/go-sqlcmd/blob/main/release/windows/msi/resources/go-sqlcmd_256.png
11 | Copyright (c) Microsoft Corporation
12 | https://github.com/microsoft/go-sqlcmd/blob/main/LICENSE
13 | false
14 | https://github.com/microsoft/go-sqlcmd
15 | https://github.com/microsoft/go-sqlcmd/tree/main/release/windows/choco
16 | https://docs.microsoft.com/sql/tools/go-sqlcmd-utility
17 | https://github.com/microsoft/go-sqlcmd/issues
18 | sqlcmd mssql sqlserver
19 | sqlcmd CLI for Microsoft SQL Server and Azure SQL
20 | sqlcmd is a multi-platform command line experience for Microsoft SQL Server and Azure SQL
21 | https://github.com/microsoft/go-sqlcmd/releases/tag/v0.8.1
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/release/windows/choco/tools/LICENSE.txt:
--------------------------------------------------------------------------------
1 | From: https://github.com/microsoft/go-sqlcmd/blob/main/LICENSE
2 |
3 | LICENSE
4 |
5 | MIT License
6 |
7 | Copyright (c) Microsoft Corporation.
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in all
17 | copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE
26 |
--------------------------------------------------------------------------------
/release/windows/choco/tools/VERIFICATION.txt:
--------------------------------------------------------------------------------
1 | VERIFICATION
2 | Verification is intended to assist the Chocolatey moderators and community
3 | in verifying that this package's contents are trustworthy.
4 |
5 | Download .msi from Microsoft Download Center
6 | https://download.microsoft.com/download/d/4/4/d4403a51-2ab7-4ea8-b850-d2710c5e1323/sqlcmd_0.8.1-1.msi
7 |
8 | Run checksum -t sha256 -f sqlcmd_0.8.1-1.msi
9 |
10 | We are the software vendor (Microsoft)
--------------------------------------------------------------------------------
/release/windows/choco/tools/chocolateyinstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop';
2 |
3 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
4 | $url = '{{DownloadUrl}}'
5 | $url64 = 'https://download.microsoft.com/download/d/4/4/d4403a51-2ab7-4ea8-b850-d2710c5e1323/sqlcmd_0.8.1-1.msi'
6 |
7 | $packageArgs = @{
8 | packageName = $env:ChocolateyPackageName
9 | unzipLocation = $toolsDir
10 | fileType = 'MSI'
11 | url = $url
12 | url64bit = $url64
13 |
14 | softwareName = 'sqlcmd*'
15 |
16 | checksum = '{{Checksum}}'
17 | checksumType = '{{ChecksumType}}'
18 | checksum64 = '03587762932D5A66ACFE15D306FE14645D53BC61162B4DA0D9AF29B4A8A1550D'
19 | checksumType64= 'sha256'
20 |
21 | silentArgs = "/qn /norestart /l*v `"$($env:TEMP)\$($packageName).$($env:chocolateyPackageVersion).MsiInstall.log`""
22 | validExitCodes= @(0, 3010, 1641)
23 | }
24 |
25 | Install-ChocolateyPackage @packageArgs
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/release/windows/msi/README.md:
--------------------------------------------------------------------------------
1 | # Windows MSI
2 |
3 | This document provides instructions on creating the MSI.
4 |
5 | ## Prerequisites
6 |
7 | 1. WIX Toolset
8 | 2. Turn on the '.NET Framework 3.5' Windows Feature (required for WIX Toolset)
9 | 3. Install [WIX Toolset build tools](http://wixtoolset.org/releases/) if not already installed
10 | 4. Install [Microsoft Build Tools](https://www.microsoft.com/download/details.aspx?id=48159)
11 |
12 | ## Building
13 |
14 | 1. Set the `CLI_VERSION` environment variable
15 | 2. Run `release\windows\msi\scripts\pipeline.cmd`
16 | 3. The unsigned MSI will be in the `.\output\msi` folder
17 |
18 | > **Note:** For `building step 1.` above set both env-vars to the same version-tag for the immediate, this will consolidated in the future.
19 |
20 | ## Release Install/Update/Uninstall Steps
21 |
22 | > **Note:** Replace `{{HOST}}` and `{{CLI_VERSION}}` with the appropriate values.
23 |
24 | ### Install `Sqlcmd Tools` on Windows
25 |
26 | The MSI distributable is used for installing or updating the `Sqlcmd Tools` CLI on Windows.
27 |
28 | [Download the MSI Installer](http://{{HOST}}/sqlcmd-{{CLI_VERSION}}.msi)
29 |
30 | When the installer asks if it can make changes to your computer, click the `Yes` box.
31 |
32 | ### Uninstall
33 |
34 | You can uninstall the `SqlCmd Tools` from the Windows _Apps and Features_ list. To uninstall:
35 |
36 | | Platform | Instructions |
37 | | ------------- |--------------------------------------------------------|
38 | | Windows 10 | Start > Settings > Apps |
39 | | Windows 8 | Start > Control Panel > Programs > Uninstall a program |
40 |
41 | The program to uninstall is listed as **Sqlcmd Tools** . Select this application, then click the `Uninstall` button.
42 |
--------------------------------------------------------------------------------
/release/windows/msi/resources/CLI_LICENSE.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri;}}
2 | {\*\generator Riched20 10.0.19041}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1
3 | \pard\widctlpar\sa160\sl252\slmult1\f0\fs22 MIT License\par
4 | Copyright (c) Microsoft Corporation.\par
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE\par
8 | }
9 |
--------------------------------------------------------------------------------
/release/windows/msi/resources/banner.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/release/windows/msi/resources/banner.bmp
--------------------------------------------------------------------------------
/release/windows/msi/resources/dialog.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/release/windows/msi/resources/dialog.bmp
--------------------------------------------------------------------------------
/release/windows/msi/resources/go-sqlcmd_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/release/windows/msi/resources/go-sqlcmd_256.png
--------------------------------------------------------------------------------
/release/windows/msi/resources/sqlcmd.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/go-sqlcmd/e8f9c267602cf2949f6b3ba2bfd7a47c891b7479/release/windows/msi/resources/sqlcmd.ico
--------------------------------------------------------------------------------
/release/windows/msi/scripts/pipeline-test.ps1:
--------------------------------------------------------------------------------
1 | #------------------------------------------------------------------------------
2 | # Copyright (c) Microsoft Corporation. All rights reserved.
3 | #------------------------------------------------------------------------------
4 |
5 | # Description:
6 | #
7 | # Instructions to be invoked under the build CI pipeline in AzureDevOps.
8 | #
9 | # Kickoff MSI package install test.
10 | #
11 | # Usage:
12 | #
13 | # set SYSTEM_ARTIFACTSDIRECTORY=\path\to\msi\sqlcmd-.msi
14 | #
15 | # $ pipeline-test.ps1
16 |
17 | if (-not (Test-Path env:CLI_VERSION)) { $env:CLI_VERSION = '0.0.1' }
18 | if (-not (Test-Path env:CLI_VERSION_REVISION)) { $env:CLI_VERSION_REVISION = '1' }
19 | if (-not (Test-Path env:ARCHITECTURE)) { $env:ARCHITECTURE = 'amd64' }
20 |
21 | tree /A /F $env:SYSTEM_ARTIFACTSDIRECTORY
22 |
23 | $msiPath = Join-Path $env:SYSTEM_ARTIFACTSDIRECTORY ("sqlcmd-" + $env:ARCHITECTURE + "_" + $env:CLI_VERSION + "-" + $env:CLI_VERSION_REVISION + ".msi")
24 |
25 | $msiPath
26 |
27 | $InstallArgs = @(
28 | "/I"
29 | $msiPath
30 | "/norestart"
31 | "/L*v"
32 | ".\install-logs.txt"
33 | "/qn"
34 | )
35 |
36 | Write-Output "Starting msi install $msiPath..."
37 | Start-Process "msiexec.exe" -ArgumentList $InstallArgs -Wait -NoNewWindow
38 | Get-Content .\install-logs.txt
39 |
40 | Write-Output "Done installing msi, checking PATH setup..."
41 | Write-Output "$env:path"
42 | $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
43 | Write-Output "$env:path"
44 |
45 | $sqlcmd = $env:ProgramFiles + "\SqlCmd\sqlcmd"
46 | & $sqlcmd --help
47 |
--------------------------------------------------------------------------------
/release/windows/msi/sqlcmd.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31729.503
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "sqlcmd", "sqlcmd.wixproj", "{10101119-735C-48F9-A3FD-B3362A78BA8A}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Debug|x64.ActiveCfg = Debug|x64
17 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Debug|x64.Build.0 = Debug|x64
18 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Debug|x86.ActiveCfg = Debug|x86
19 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Debug|x86.Build.0 = Debug|x86
20 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Release|x64.ActiveCfg = Release|x64
21 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Release|x64.Build.0 = Release|x64
22 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Release|x86.ActiveCfg = Release|x86
23 | {10101119-735C-48F9-A3FD-B3362A78BA8A}.Release|x86.Build.0 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {D9594663-3E11-4507-AEEB-AB2714988330}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/testdata/sql.txt:
--------------------------------------------------------------------------------
1 | select 1 as col1
2 | go
3 |
4 |
--------------------------------------------------------------------------------