Skip to content

Initial version of go-sqlcmd #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
45312fe
commandline and variables
shueybubbles Jun 30, 2021
e632275
add displayname to build tasks
shueybubbles Jun 30, 2021
cbe3fde
implement quit command
shueybubbles Jul 6, 2021
08fe2f1
initial version of batch parsing
shueybubbles Jul 9, 2021
d57b7dc
move variables
shueybubbles Jul 9, 2021
5ad1855
new package for variables
shueybubbles Jul 12, 2021
659791a
add vscode helpers for debug
shueybubbles Jul 12, 2021
1482ee6
fix go and quit processing
shueybubbles Jul 12, 2021
9068e60
implement Out and Error commands
shueybubbles Jul 14, 2021
ff2742e
fix custom batch separator
shueybubbles Jul 14, 2021
9755691
move connectionString to sqlcmd
shueybubbles Jul 20, 2021
a816a31
Add sql connection and print column headers
shueybubbles Aug 1, 2021
3e9b185
add row and error processing
shueybubbles Aug 3, 2021
b5534ef
remove unused package
shueybubbles Aug 3, 2021
0c3062a
fix binary rendering and screen fitting
shueybubbles Aug 4, 2021
a799fd5
rewrite decodeBinary for performance
shueybubbles Aug 4, 2021
73469fc
fix test pipeline
shueybubbles Aug 13, 2021
7e8320a
remove password command line param
shueybubbles Aug 13, 2021
3ea9057
exit on ctrl-c
shueybubbles Aug 13, 2021
37de0ad
implement -q and -Q
shueybubbles Aug 16, 2021
62f2811
de-lint and update readme
shueybubbles Aug 17, 2021
6283619
add lint for PRs
shueybubbles Aug 17, 2021
605d484
separate pkg and cmd folders
shueybubbles Aug 24, 2021
126c3b1
fix pipeline for new folder layout
shueybubbles Aug 24, 2021
b82e517
de-lint and reduce public surface
shueybubbles Aug 25, 2021
6b66e5f
more de-linting
shueybubbles Aug 25, 2021
b9bd11b
hopefully last round of de-linting
shueybubbles Aug 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: golangci-lint
on:
push:
branches:
- main
pull_request:
jobs:
golangci-pr:
name: lint-pr-changes
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.42.0
only-new-issues: true
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@

# Dependency directories (remove the comment below to include it)
# vendor/

coverage.json
coverage.txt
coverage.xml
testresults.xml
80 changes: 80 additions & 0 deletions .pipelines/TestSql2017.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
pool:
vmImage: 'ubuntu-latest'

steps:
- task: GoTool@0
inputs:
version: '1.16.5'
- task: Go@0
displayName: 'Go: get dependencies'
inputs:
command: 'get'
arguments: '-d'
workingDirectory: '$(Build.SourcesDirectory)/cmd/sqlcmd'


- task: Go@0
displayName: 'Go: install gotest.tools/gotestsum'
inputs:
command: 'custom'
customCommand: 'install'
arguments: 'gotest.tools/gotestsum@latest'
workingDirectory: '$(System.DefaultWorkingDirectory)'

- task: Go@0
displayName: 'Go: install github.com/axw/gocov/gocov'
inputs:
command: 'custom'
customCommand: 'install'
arguments: 'github.com/axw/gocov/gocov@latest'
workingDirectory: '$(System.DefaultWorkingDirectory)'

- task: Go@0
displayName: 'Go: install github.com/axw/gocov/gocov'
inputs:
command: 'custom'
customCommand: 'install'
arguments: 'github.com/AlekSi/gocov-xml@latest'
workingDirectory: '$(System.DefaultWorkingDirectory)'

#Your build pipeline references an undefined variables named SQLPASSWORD.
#Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972

- task: Docker@2
displayName: 'Run SQL 2017 docker image'
inputs:
command: run
arguments: '-m 2GB -e ACCEPT_EULA=1 -d --name sql2017 -p:1433:1433 -e SA_PASSWORD=$(SQLPASSWORD) mcr.microsoft.com/mssql/server:2017-latest'

- script: |
~/go/bin/gotestsum --junitfile testresults.xml -- ./... -coverprofile=coverage.txt -covermode count
~/go/bin/gocov convert coverage.txt > coverage.json
~/go/bin/gocov-xml < coverage.json > coverage.xml
mkdir coverage
workingDirectory: '$(Build.SourcesDirectory)'
displayName: 'run tests'
env:
SQLPASSWORD: $(SQLPASSWORD)
SQLCMDUSER: sa
SQLCMDPASSWORD: $(SQLPASSWORD)
continueOnError: true
- task: PublishTestResults@2
displayName: "Publish junit-style results"
inputs:
testResultsFiles: 'testresults.xml'
testResultsFormat: JUnit
searchFolder: '$(Build.SourcesDirectory)'
testRunTitle: 'SQL 2017 - $(Build.SourceBranchName)'
condition: always()
continueOnError: true

- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
pathToSources: '$(Build.SourcesDirectory)'
summaryFileLocation: $(Build.SourcesDirectory)/**/coverage.xml
reportDirectory: $(Build.SourcesDirectory)/**/coverage
failIfCoverageEmpty: true
condition: always()
continueOnError: true

27 changes: 27 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach using delve",
"type": "go",
"request": "attach",
"preLaunchTask": "delve",
"mode": "remote",
"remotePath": "${workspaceFolder}",
"port" : 23456,
"host" : "127.0.0.1",
"cwd" : "${workspaceFolder}",
},
{
"name" : "Run query and exit",
"type" : "go",
"request": "launch",
"mode" : "auto",
"program": "${fileDirname}",
"args" : ["-Q", "\"select 100 as Count\""],
}
]
}
36 changes: 36 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "delve",
"type": "shell",
"command": "dlv debug --headless --listen=:23456 --api-version=2 \"${workspaceFolder}\"",
"isBackground": true,
"presentation": {
"focus": true,
"panel": "dedicated",
"clear": false
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": {
"pattern": {
"regexp": ""
},
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": ".*"
},
"endsPattern": {
"regexp": ".*server listening.*"
}
}
}
}
]
}
36 changes: 26 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
# Project
# SQL Utilities - Go edition

> This repo has been populated by an initial template to help get you started. Please
> make sure to update the content to build a great experience for community-building.
This repo contains command line tools and go packages for working with Microsoft SQL Server, Azure SQL Database, and Azure Synapse.

As the maintainer of this project, please make a few updates:
## Sqlcmd

- Improving this README.MD file to provide a great experience
- Updating SUPPORT.MD with content about this project's support experience
- Understanding the security reporting process in SECURITY.MD
- Remove this section from the README
The `sqlcmd` project aims to be a complete port of the native sqlcmd to the `go` language, utilizing the [go-mssqldb](https://github.com/denisenkom/go-mssqldb) driver. For full documentation of the tool, see https://docs.microsoft.com/sql/tools/sqlcmd-utility

### Breaking changes

We will be implementing as many command line switches and behaviors as possible over time. Several switches and behaviors are expected to change in this implementation.

- `-P` switch will be removed. Passwords for SQL authentication can only be provided through these mechanisms:

-The `SQLCMDPASSWORD` environment variable
-The `:CONNECT` command
-When prompted, the user can type the password to complete a connection

- `-R` switch will be removed. The go runtime does not provide access to user locale information, and it's not readily available through syscall on all supported platforms.
- Some behaviors that were kept to maintain compatibility with `OSQL` may be changed, such as alignment of column headers for some data types.

### Packages

#### sqlcmd

#### batch

## Contributing

Expand All @@ -26,8 +41,9 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio

## Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.

129 changes: 129 additions & 0 deletions cmd/sqlcmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package main

import (
"fmt"
"os"

"github.com/alecthomas/kong"
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
"github.com/xo/usql/rline"
)

// SQLCmdArguments defines the command line arguments for sqlcmd
// The exhaustive list is at https://docs.microsoft.com/sql/tools/sqlcmd-utility?view=sql-server-ver15
type SQLCmdArguments struct {
// Which batch terminator to use. Default is GO
BatchTerminator string `short:"c" default:"GO" arghelp:"Specifies the batch terminator. The default value is GO."`
// Whether to trust the server certificate on an encrypted connection
TrustServerCertificate bool `short:"C" help:"Implicitly trust the server certificate without validation."`
DatabaseName string `short:"d" help:"This option sets the sqlcmd scripting variable SQLCMDDBNAME. This parameter specifies the initial database. The default is your login's default-database property. If the database does not exist, an error message is generated and sqlcmd exits."`
UseTrustedConnection bool `short:"E" xor:"uid" help:"Uses a trusted connection instead of using a user name and password to sign in to SQL Server, ignoring any any environment variables that define user name and password."`
UserName string `short:"U" xor:"uid" help:"The login name or contained database user name. For contained database users, you must provide the database name option"`
// Files from which to read query text
InputFile []string `short:"i" xor:"input1, input2" type:"existingFile" help:"Identifies one or more files that contain batches of SQL statements. If one or more files do not exist, sqlcmd will exit. Mutually exclusive with -Q/-q."`
OutputFile string `short:"o" type:"path" help:"Identifies the file that receives output from sqlcmd."`
// First query to run in interactive mode
InitialQuery string `short:"q" xor:"input1" help:"Executes a query when sqlcmd starts, but does not exit sqlcmd when the query has finished running. Multiple-semicolon-delimited queries can be executed."`
// Query to run then exit
Query string `short:"Q" xor:"input2" help:"Executes a query when sqlcmd starts and then immediately exits sqlcmd. Multiple-semicolon-delimited queries can be executed."`
Server string `short:"S" help:"[tcp:]server[\\instance_name][,port]Specifies the instance of SQL Server to which to connect. It sets the sqlcmd scripting variable SQLCMDSERVER."`
// Disable syscommands with a warning
DisableCmdAndWarn bool `short:"X" xor:"syscmd" help:"Disables commands that might compromise system security. Sqlcmd issues a warning and continues."`
}

// Breaking changes in command line are listed here.
// Any switch not listed in breaking changes and not also included in SqlCmdArguments just has not been implemented yet
// 1. -P: Passwords have to be provided through SQLCMDPASSWORD environment variable or typed when prompted
// 2. -R: Go runtime doesn't expose user locale information and syscall would only enable it on Windows, so we won't try to implement it

var args SQLCmdArguments

func main() {
kong.Parse(&args)
vars := sqlcmd.InitializeVariables(!args.DisableCmdAndWarn)
setVars(vars, &args)

exitCode, err := run(vars)
if err != nil {
fmt.Println(err.Error())
}
os.Exit(exitCode)
}

// Initializes scripting variables from command line arguments
func setVars(vars *sqlcmd.Variables, args *SQLCmdArguments) {
varmap := map[string]func(*SQLCmdArguments) string{
sqlcmd.SQLCMDDBNAME: func(a *SQLCmdArguments) string { return a.DatabaseName },
sqlcmd.SQLCMDLOGINTIMEOUT: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDUSEAAD: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDWORKSTATION: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDSERVER: func(a *SQLCmdArguments) string { return a.Server },
sqlcmd.SQLCMDERRORLEVEL: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDPACKETSIZE: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDUSER: func(a *SQLCmdArguments) string { return a.UserName },
sqlcmd.SQLCMDSTATTIMEOUT: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDHEADERS: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDCOLSEP: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDCOLWIDTH: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDMAXVARTYPEWIDTH: func(a *SQLCmdArguments) string { return "" },
sqlcmd.SQLCMDMAXFIXEDTYPEWIDTH: func(a *SQLCmdArguments) string { return "" },
}
for varname, set := range varmap {
val := set(args)
if val != "" {
vars.Set(varname, val)
}
}
}

func run(vars *sqlcmd.Variables) (exitcode int, err error) {
wd, err := os.Getwd()
if err != nil {
return 1, err
}
if args.BatchTerminator != "GO" {
err = sqlcmd.SetBatchTerminator(args.BatchTerminator)
if err != nil {
err = fmt.Errorf("invalid batch terminator '%s'", args.BatchTerminator)
}
}
if err != nil {
return 1, err
}

iactive := args.InputFile == nil
line, err := rline.New(!iactive, "", "")
if err != nil {
return 1, err
}
defer line.Close()

s := sqlcmd.New(line, wd, vars)
s.Connect.UseTrustedConnection = args.UseTrustedConnection
s.Connect.TrustServerCertificate = args.TrustServerCertificate
s.Format = sqlcmd.NewSQLCmdDefaultFormatter(false)
if args.OutputFile != "" {
err = s.RunCommand(sqlcmd.Commands["OUT"], []string{args.OutputFile})
if err != nil {
return 1, err
}
}
once := false
if args.InitialQuery != "" {
s.Query = args.InitialQuery
} else if args.Query != "" {
once = true
s.Query = args.Query
}
err = s.ConnectDb("", "", "", !iactive)
if err != nil {
return 1, err
}
if iactive {
err = s.Run(once)
}
return s.Exitcode, err
}
Loading