Compare commits

...

15 Commits

Author SHA1 Message Date
cthuang 5e2b43adb5 Release 2021.2.4 2021-02-23 12:47:21 +00:00
cthuang c7dca16300 Publish release notes for 2021.2.4 2021-02-23 12:39:18 +00:00
cthuang 9d5bd256be TUN-3964: Revert "TUN-3922: Repoint urfave/cli/v2 library at patched branch at github.com/ipostelnik/cli/v2@fixed which correctly handles reading flags declared at multiple levels of subcommands."
This reverts commit 1670ee87fb.
2021-02-23 12:32:07 +00:00
Nuno Diegues e9c2afec56 TUN-3948: Log error when retrying connection 2021-02-23 11:40:29 +00:00
cthuang 4e33281337 Release 2021.2.3 2021-02-23 10:28:46 +00:00
cthuang ac559f86c9 Publish release notes for 2021.2.3 2021-02-23 10:17:43 +00:00
Areg Harutyunyan 117766562b TUN-3945: Fix runApp signature for generic service 2021-02-19 22:06:57 +00:00
jmonson 5d76e940c7 Added support for proxy (#318)
Co-authored-by: Jeff Monson <jeff.monson@armis.com>
2021-02-19 21:27:22 +04:00
Igor Postelnik 1670ee87fb TUN-3922: Repoint urfave/cli/v2 library at patched branch at github.com/ipostelnik/cli/v2@fixed which correctly handles reading flags declared at multiple levels of subcommands. 2021-02-18 18:12:20 +00:00
Igor Postelnik a8ae6de213 TUN-3924: Removed db-connect command. Added a placeholder handler for this command that informs users that command is no longer supported. 2021-02-17 20:13:51 -06:00
David Jimenez d7c4a89106 Add max upstream connections dns-proxy option (#290)
* Add max upstream connections dns-proxy option

Allows defining a limit to the number of connections that can be
established with the upstream DNS host.

If left unset, there may be situations where connections fail to
establish, which causes the Transport to create an influx of connections
causing upstream to throttle our requests and triggering a runaway
effect resulting in high CPU usage. See https://github.com/cloudflare/cloudflared/issues/91

* Code review with proposed changes

* Add max upstream connections flag to tunnel flags

* Reduce DNS proxy max upstream connections default value

Reduce the default value of maximum upstream connections on the DNS
proxy to guarantee it works on single-core and other low-end hardware.
Further testing could allow for a safe increase of this value.

* Update dns-proxy flag name

Also remove `MaxUpstreamConnsFlag` const as it's no longer referenced in more than one place and to make things more consistent with how the other flags are referenced.

Co-authored-by: Adam Chalmers <achalmers@cloudflare.com>
2021-02-12 21:32:29 +04:00
Nuno Diegues e7354f4768 Update the TryCloudflare link 2021-02-11 15:19:28 +00:00
Samuel Rhea 18ec338d4c Update README.md
Updates to the new Cloudflare documentation links
2021-02-11 15:19:28 +00:00
Adam Chalmers 7f97e2f030 TUN-3913: Help gives wrong exit code for autoupdate 2021-02-11 08:39:56 -06:00
Adam Chalmers a278753bbf TUN-3902: Add jitter to backoffhandler
Jitter is important to avoid every cloudflared in the world trying to
reconnect at t=1, 2, 4, etc. That could overwhelm the backend. But
if each cloudflared randomly waits for up to 2, then up to 4, then up
to 8 etc, then the retries get spread out evenly across time.

On average, wait times should be the same (e.g. instead of waiting for
exactly 1 second, cloudflared will wait betweeen 0 and 2 seconds).

This is the "Full Jitter" algorithm from https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
2021-02-11 14:36:13 +00:00
316 changed files with 190 additions and 345410 deletions
+22
View File
@@ -0,0 +1,22 @@
# Experimental: This is a new format for release notes. The format and availability is subject to change.
## 2021.2.4
### Bug Fixes
- [configuration] fixes a case where `cloudflared` failed to read URLs in configuration files
- [logging] cloudflared now logs reason for failed connections if the error is recoverable
## 2021.2.3
### Backward Incompatible Changes
- [db-connect] Removes db-connect from `cloudflared`. The Cloudflare Workers product will continue to support db-connect implementations with versions of `cloudflared` that predate this release and include support for db-connect.
### New Features
- [TCP connects] Introduces support for proxy configurations with websockets in arbitrary TCP connections (#318)
### Improvements
- [command line interface] Nested commands (such as cloudflared tunnel run) now consider CLI arguments even if they appear earlier in the command. E.g. `cloudflared --config config.yaml tunnel run` will now behave similarly to `cloudflared tunnel --config config.yaml run`
### Bug Fixes
- [dns-proxy] The maximum number of upstream connections is now limited by default which should fix reported issues of cloudflared exhausting CPU usage when faced with connectivity issues.
+9 -9
View File
@@ -1,6 +1,6 @@
# Argo Tunnel client
Contains the command-line client for Argo Tunnel, a tunneling daemon that proxies any local webserver through the Cloudflare network. Extensive documentation can be found in the [Argo Tunnel section](https://developers.cloudflare.com/argo-tunnel/) of the Cloudflare Docs.
Contains the command-line client for Argo Tunnel, a tunneling daemon that proxies any local webserver through the Cloudflare network. Extensive documentation can be found in the [Argo Tunnel section](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) of the Cloudflare Docs.
## Before you get started
@@ -13,29 +13,29 @@ Before you use Argo Tunnel, you'll need to complete a few steps in the Cloudflar
Downloads are available as standalone binaries, a Docker image, and Debian, RPM, and Homebrew packages. You can also find releases here on the `cloudflared` GitHub repository.
* You can [install on macOS](https://developers.cloudflare.com/argo-tunnel/getting-started/installation#macos) via Homebrew or by downloading the [latest Darwin amd64 release](https://github.com/cloudflare/cloudflared/releases)
* Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/argo-tunnel/getting-started/installation#linux)
* You can [install on macOS](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#macos) via Homebrew or by downloading the [latest Darwin amd64 release](https://github.com/cloudflare/cloudflared/releases)
* Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#linux)
* A Docker image of `cloudflared` is [available on DockerHub](https://hub.docker.com/r/cloudflare/cloudflared)
* You can install on Windows machines with the [steps here](https://developers.cloudflare.com/argo-tunnel/getting-started/installation#windows)
* You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#windows)
User documentation for Argo Tunnel can be found at https://developers.cloudflare.com/argo-tunnel/
User documentation for Argo Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
## Creating Tunnels and routing traffic
Once installed, you can authenticate `cloudflared` into your Cloudflare account and begin creating Tunnels that serve traffic for hostnames in your account.
* Create a Tunnel with [these instructions](https://developers.cloudflare.com/argo-tunnel/create-tunnel)
* Route traffic to that Tunnel with [DNS records in Cloudflare](https://developers.cloudflare.com/argo-tunnel/routing-to-tunnel/dns) or with a [Cloudflare Load Balancer](https://developers.cloudflare.com/argo-tunnel/routing-to-tunnel/lb)
* Create a Tunnel with [these instructions](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/create-tunnel)
* Route traffic to that Tunnel with [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns) or with a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb)
## TryCloudflare
Want to test Argo Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/argo-tunnel/learning/trycloudflare).
Want to test Argo Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/trycloudflare).
## Deprecated versions
Cloudflare currently supports all versions of `cloudflared`. Starting on March 20, 2021, Cloudflare will no longer support versions released prior to 2020.5.1.
All features available in versions released prior to 2020.5.1 are available in current versions. Breaking changes unrelated to feature availability may be introduced that will impact versions released prior to 2020.5.1. You can read more about upgrading `cloudflared` in our [developer documentation](https://developers.cloudflare.com/argo-tunnel/getting-started/installation#updating-cloudflared).
All features available in versions released prior to 2020.5.1 are available in current versions. Breaking changes unrelated to feature availability may be introduced that will impact versions released prior to 2020.5.1. You can read more about upgrading `cloudflared` in our [developer documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#updating-cloudflared).
| Version(s) | Deprecation status |
|---|---|
+17
View File
@@ -1,3 +1,20 @@
2021.2.4
- 2021-02-22 TUN-3948: Log error when retrying connection
- 2021-02-23 TUN-3964: Revert "TUN-3922: Repoint urfave/cli/v2 library at patched branch at github.com/ipostelnik/cli/v2@fixed which correctly handles reading flags declared at multiple levels of subcommands."
- 2021-02-23 Publish release notes for 2021.2.4
2021.2.3
- 2021-02-23 Publish release notes for 2021.2.3
- 2021-02-10 TUN-3902: Add jitter to backoffhandler
- 2021-02-11 TUN-3913: Help gives wrong exit code for autoupdate
- 2021-02-12 Add max upstream connections dns-proxy option (#290)
- 2021-02-16 TUN-3924: Removed db-connect command. Added a placeholder handler for this command that informs users that command is no longer supported.
- 2021-02-12 TUN-3922: Repoint urfave/cli/v2 library at patched branch at github.com/ipostelnik/cli/v2@fixed which correctly handles reading flags declared at multiple levels of subcommands.
- 2021-02-19 Added support for proxy (#318)
- 2021-02-19 TUN-3945: Fix runApp signature for generic service
- 2021-02-09 Update README.md
- 2021-02-09 Update the TryCloudflare link
2021.2.2
- 2021-02-04 TUN-3864: Users can choose where credentials file is written after creating a tunnel
- 2021-02-04 TUN-3869: Improve reliability of graceful shutdown.
+2 -12
View File
@@ -194,6 +194,7 @@ stretch: &stretch
builddeps:
- *pinned_go_fips
- build-essential
- gotest-to-teamcity
post-cache:
- export GOOS=linux
- export GOARCH=amd64
@@ -201,7 +202,7 @@ stretch: &stretch
# cd to a non-module directory: https://github.com/golang/go/issues/24250
- (cd / && go get github.com/BurntSushi/go-sumtype)
- export PATH="$HOME/go/bin:$PATH"
- make test
- make test | gotest-to-teamcity
update-homebrew:
builddeps:
- openssh-client
@@ -275,14 +276,3 @@ centos-7:
- export GOOS=linux
- export GOARCH=amd64
- make cloudflared-rpm
# cfsetup compose
default-stack: test_dbconnect
test_dbconnect:
compose:
up-args:
- --renew-anon-volumes
- --abort-on-container-exit
- --exit-code-from=cloudflared
files:
- dbconnect_tests/dbconnect.yaml
+5 -3
View File
@@ -11,8 +11,9 @@ const (
// ResolverServiceType is used to identify what kind of overwatch service this is
ResolverServiceType = "resolver"
LogFieldResolverAddress = "resolverAddress"
LogFieldResolverPort = "resolverPort"
LogFieldResolverAddress = "resolverAddress"
LogFieldResolverPort = "resolverPort"
LogFieldResolverMaxUpstreamConns = "resolverMaxUpstreamConns"
)
// ResolverService is used to wrap the tunneldns package's DNS over HTTP
@@ -57,7 +58,7 @@ func (s *ResolverService) Shutdown() {
func (s *ResolverService) Run() error {
// create a listener
l, err := tunneldns.CreateListener(s.resolver.AddressOrDefault(), s.resolver.PortOrDefault(),
s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault(), s.log)
s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault(), s.resolver.MaxUpstreamConnectionsOrDefault(), s.log)
if err != nil {
return err
}
@@ -74,6 +75,7 @@ func (s *ResolverService) Run() error {
resolverLog := s.log.With().
Str(LogFieldResolverAddress, s.resolver.AddressOrDefault()).
Uint16(LogFieldResolverPort, s.resolver.PortOrDefault()).
Int(LogFieldResolverMaxUpstreamConns, s.resolver.MaxUpstreamConnectionsOrDefault()).
Logger()
resolverLog.Info().Msg("Starting resolver")
+21
View File
@@ -0,0 +1,21 @@
package cliutil
import (
"fmt"
"github.com/urfave/cli/v2"
)
func RemovedCommand(name string) *cli.Command {
return &cli.Command{
Name: name,
Action: ErrorHandler(func(context *cli.Context) error {
return cli.Exit(
fmt.Sprintf("%s command is no longer supported by cloudflared. Consult Argo Tunnel documentation for possible alternative solutions.", name),
-1,
)
}),
Description: fmt.Sprintf("%s is deprecated", name),
Hidden: true,
}
}
+17 -5
View File
@@ -5,6 +5,8 @@ import (
"fmt"
"io"
"strings"
"github.com/cloudflare/cloudflared/tunneldns"
)
// Forwarder represents a client side listener to forward traffic to the edge
@@ -25,11 +27,12 @@ type Tunnel struct {
// DNSResolver represents a client side DNS resolver
type DNSResolver struct {
Enabled bool `json:"enabled"`
Address string `json:"address,omitempty"`
Port uint16 `json:"port,omitempty"`
Upstreams []string `json:"upstreams,omitempty"`
Bootstraps []string `json:"bootstraps,omitempty"`
Enabled bool `json:"enabled"`
Address string `json:"address,omitempty"`
Port uint16 `json:"port,omitempty"`
Upstreams []string `json:"upstreams,omitempty"`
Bootstraps []string `json:"bootstraps,omitempty"`
MaxUpstreamConnections int `json:"max_upstream_connections,omitempty"`
}
// Root is the base options to configure the service
@@ -59,6 +62,7 @@ func (r *DNSResolver) Hash() string {
io.WriteString(h, strings.Join(r.Bootstraps, ","))
io.WriteString(h, strings.Join(r.Upstreams, ","))
io.WriteString(h, fmt.Sprintf("%d", r.Port))
io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections))
io.WriteString(h, fmt.Sprintf("%v", r.Enabled))
return fmt.Sprintf("%x", h.Sum(nil))
}
@@ -99,3 +103,11 @@ func (r *DNSResolver) BootstrapsOrDefault() []string {
}
return []string{"https://162.159.36.1/dns-query", "https://162.159.46.1/dns-query", "https://[2606:4700:4700::1111]/dns-query", "https://[2606:4700:4700::1001]/dns-query"}
}
// MaxUpstreamConnectionsOrDefault return the max upstream connections or returns the default if negative
func (r *DNSResolver) MaxUpstreamConnectionsOrDefault() int {
if r.MaxUpstreamConnections >= 0 {
return r.MaxUpstreamConnections
}
return tunneldns.MaxUpstreamConnsDefault
}
+1 -1
View File
@@ -8,6 +8,6 @@ import (
cli "github.com/urfave/cli/v2"
)
func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
func runApp(app *cli.App, graceShutdownC chan struct{}) {
app.Run(os.Args)
}
+3 -1
View File
@@ -2,6 +2,7 @@ package main
import (
"fmt"
"math/rand"
"strings"
"time"
@@ -43,6 +44,7 @@ var (
)
func main() {
rand.Seed(time.Now().UnixNano())
metrics.RegisterBuildInfo(BuildTime, Version)
raven.SetRelease(Version)
@@ -115,7 +117,7 @@ func commands(version func(c *cli.Context)) []*cli.Command {
If a new version exists, updates the agent binary and quits.
Otherwise, does nothing.
To determine if an update happened in a script, check for error code 64.`,
To determine if an update happened in a script, check for error code 11.`,
},
{
Name: "version",
+9 -30
View File
@@ -19,7 +19,6 @@ import (
"github.com/cloudflare/cloudflared/cmd/cloudflared/ui"
"github.com/cloudflare/cloudflared/cmd/cloudflared/updater"
"github.com/cloudflare/cloudflared/connection"
"github.com/cloudflare/cloudflared/dbconnect"
"github.com/cloudflare/cloudflared/ingress"
"github.com/cloudflare/cloudflared/logger"
"github.com/cloudflare/cloudflared/metrics"
@@ -106,14 +105,14 @@ func Commands() []*cli.Command {
buildCleanupCommand(),
// for compatibility, allow following as tunnel subcommands
tunneldns.Command(true),
dbConnectCmd(),
cliutil.RemovedCommand("db-connect"),
}
return []*cli.Command{
buildTunnelCommand(subcommands),
// for compatibility, allow following as top-level subcommands
buildLoginSubcommand(true),
dbConnectCmd(),
cliutil.RemovedCommand("db-connect"),
}
}
@@ -479,33 +478,6 @@ func addPortIfMissing(uri *url.URL, port int) string {
return fmt.Sprintf("%s:%d", uri.Hostname(), port)
}
func dbConnectCmd() *cli.Command {
cmd := dbconnect.Cmd()
// Append the tunnel commands so users can customize the daemon settings.
cmd.Flags = appendFlags(Flags(), cmd.Flags...)
// Override before to run tunnel validation before dbconnect validation.
cmd.Before = func(c *cli.Context) error {
err := SetFlagsFromConfigFile(c)
if err == nil {
err = dbconnect.CmdBefore(c)
}
return err
}
// Override action to setup the Proxy, then if successful, start the tunnel daemon.
cmd.Action = cliutil.ErrorHandler(func(c *cli.Context) error {
err := dbconnect.CmdAction(c)
if err == nil {
err = TunnelCommand(c)
}
return err
})
return cmd
}
// appendFlags will append extra flags to a slice of flags.
//
// The cli package will panic if two flags exist with the same name,
@@ -1011,6 +983,13 @@ func configureProxyDNSFlags(shouldHide bool) []cli.Flag {
EnvVars: []string{"TUNNEL_DNS_UPSTREAM"},
Hidden: shouldHide,
}),
altsrc.NewIntFlag(&cli.IntFlag{
Name: "proxy-dns-max-upstream-conns",
Usage: "Maximum concurrent connections to upstream. Setting to 0 means unlimited.",
Value: tunneldns.MaxUpstreamConnsDefault,
Hidden: shouldHide,
EnvVars: []string{"TUNNEL_DNS_MAX_UPSTREAM_CONNS"},
}),
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
Name: "proxy-dns-bootstrap",
Usage: "bootstrap endpoint URL, you can specify multiple endpoints for redundancy.",
+7 -1
View File
@@ -1,6 +1,8 @@
package tunnel
import (
"fmt"
"github.com/cloudflare/cloudflared/tunneldns"
"github.com/pkg/errors"
@@ -13,7 +15,11 @@ func runDNSProxyServer(c *cli.Context, dnsReadySignal chan struct{}, shutdownC <
if port <= 0 || port > 65535 {
return errors.New("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
}
listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap"), log)
maxUpstreamConnections := c.Int("proxy-dns-max-upstream-conns")
if maxUpstreamConnections < 0 {
return fmt.Errorf("'%s' must be 0 or higher", "proxy-dns-max-upstream-conns")
}
listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap"), maxUpstreamConnections, log)
if err != nil {
close(dnsReadySignal)
listener.Stop()
-145
View File
@@ -1,145 +0,0 @@
package dbconnect
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strings"
"time"
"unicode"
"unicode/utf8"
)
// Client is an interface to talk to any database.
//
// Currently, the only implementation is SQLClient, but its structure
// should be designed to handle a MongoClient or RedisClient in the future.
type Client interface {
Ping(context.Context) error
Submit(context.Context, *Command) (interface{}, error)
}
// NewClient creates a database client based on its URL scheme.
func NewClient(ctx context.Context, originURL *url.URL) (Client, error) {
return NewSQLClient(ctx, originURL)
}
// Command is a standard, non-vendor format for submitting database commands.
//
// When determining the scope of this struct, refer to the following litmus test:
// Could this (roughly) conform to SQL, Document-based, and Key-value command formats?
type Command struct {
Statement string `json:"statement"`
Arguments Arguments `json:"arguments,omitempty"`
Mode string `json:"mode,omitempty"`
Isolation string `json:"isolation,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
}
// Validate enforces the contract of Command: non empty statement (both in length and logic),
// lowercase mode and isolation, non-zero timeout, and valid Arguments.
func (cmd *Command) Validate() error {
if cmd.Statement == "" {
return fmt.Errorf("cannot provide an empty statement")
}
if strings.Map(func(char rune) rune {
if char == ';' || unicode.IsSpace(char) {
return -1
}
return char
}, cmd.Statement) == "" {
return fmt.Errorf("cannot provide a statement with no logic: '%s'", cmd.Statement)
}
cmd.Mode = strings.ToLower(cmd.Mode)
cmd.Isolation = strings.ToLower(cmd.Isolation)
if cmd.Timeout.Nanoseconds() <= 0 {
cmd.Timeout = 24 * time.Hour
}
return cmd.Arguments.Validate()
}
// UnmarshalJSON converts a byte representation of JSON into a Command, which is also validated.
func (cmd *Command) UnmarshalJSON(data []byte) error {
// Alias is required to avoid infinite recursion from the default UnmarshalJSON.
type Alias Command
alias := &struct {
*Alias
}{
Alias: (*Alias)(cmd),
}
err := json.Unmarshal(data, &alias)
if err == nil {
err = cmd.Validate()
}
return err
}
// Arguments is a wrapper for either map-based or array-based Command arguments.
//
// Each field is mutually-exclusive and some Client implementations may not
// support both fields (eg. MySQL does not accept named arguments).
type Arguments struct {
Named map[string]interface{}
Positional []interface{}
}
// Validate enforces the contract of Arguments: non nil, mutually exclusive, and no empty or reserved keys.
func (args *Arguments) Validate() error {
if args.Named == nil {
args.Named = map[string]interface{}{}
}
if args.Positional == nil {
args.Positional = []interface{}{}
}
if len(args.Named) > 0 && len(args.Positional) > 0 {
return fmt.Errorf("both named and positional arguments cannot be specified: %+v and %+v", args.Named, args.Positional)
}
for key := range args.Named {
if key == "" {
return fmt.Errorf("named arguments cannot contain an empty key: %+v", args.Named)
}
if !utf8.ValidString(key) {
return fmt.Errorf("named argument does not conform to UTF-8 encoding: %s", key)
}
if strings.HasPrefix(key, "_") {
return fmt.Errorf("named argument cannot start with a reserved keyword '_': %s", key)
}
if unicode.IsNumber([]rune(key)[0]) {
return fmt.Errorf("named argument cannot start with a number: %s", key)
}
}
return nil
}
// UnmarshalJSON converts a byte representation of JSON into Arguments, which is also validated.
func (args *Arguments) UnmarshalJSON(data []byte) error {
var obj interface{}
err := json.Unmarshal(data, &obj)
if err != nil {
return err
}
named, ok := obj.(map[string]interface{})
if ok {
args.Named = named
} else {
positional, ok := obj.([]interface{})
if ok {
args.Positional = positional
} else {
return fmt.Errorf("arguments must either be an object {\"0\":\"val\"} or an array [\"val\"]: %s", string(data))
}
}
return args.Validate()
}
-183
View File
@@ -1,183 +0,0 @@
package dbconnect
import (
"encoding/json"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCommandValidateEmpty(t *testing.T) {
stmts := []string{
"",
";",
" \n\t",
";\n;\t;",
}
for _, stmt := range stmts {
cmd := Command{Statement: stmt}
assert.Error(t, cmd.Validate(), stmt)
}
}
func TestCommandValidateMode(t *testing.T) {
modes := []string{
"",
"query",
"ExEc",
"PREPARE",
}
for _, mode := range modes {
cmd := Command{Statement: "Ok", Mode: mode}
assert.NoError(t, cmd.Validate(), mode)
assert.Equal(t, strings.ToLower(mode), cmd.Mode)
}
}
func TestCommandValidateIsolation(t *testing.T) {
isos := []string{
"",
"default",
"read_committed",
"SNAPshot",
}
for _, iso := range isos {
cmd := Command{Statement: "Ok", Isolation: iso}
assert.NoError(t, cmd.Validate(), iso)
assert.Equal(t, strings.ToLower(iso), cmd.Isolation)
}
}
func TestCommandValidateTimeout(t *testing.T) {
cmd := Command{Statement: "Ok", Timeout: 0}
assert.NoError(t, cmd.Validate())
assert.NotZero(t, cmd.Timeout)
cmd = Command{Statement: "Ok", Timeout: 1 * time.Second}
assert.NoError(t, cmd.Validate())
assert.Equal(t, 1*time.Second, cmd.Timeout)
}
func TestCommandValidateArguments(t *testing.T) {
cmd := Command{Statement: "Ok", Arguments: Arguments{
Named: map[string]interface{}{"key": "val"},
Positional: []interface{}{"val"},
}}
assert.Error(t, cmd.Validate())
}
func TestCommandUnmarshalJSON(t *testing.T) {
strs := []string{
"{\"statement\":\"Ok\"}",
"{\"statement\":\"Ok\",\"arguments\":[0, 3.14, \"apple\"],\"mode\":\"query\"}",
"{\"statement\":\"Ok\",\"isolation\":\"read_uncommitted\",\"timeout\":1000}",
}
for _, str := range strs {
var cmd Command
assert.NoError(t, json.Unmarshal([]byte(str), &cmd), str)
}
strs = []string{
"",
"\"",
"{}",
"{\"argument\":{\"key\":\"val\"}}",
"{\"statement\":[\"Ok\"]}",
}
for _, str := range strs {
var cmd Command
assert.Error(t, json.Unmarshal([]byte(str), &cmd), str)
}
}
func TestArgumentsValidateNotNil(t *testing.T) {
args := Arguments{}
assert.NoError(t, args.Validate())
assert.NotNil(t, args.Named)
assert.NotNil(t, args.Positional)
}
func TestArgumentsValidateMutuallyExclusive(t *testing.T) {
args := []Arguments{
Arguments{},
Arguments{Named: map[string]interface{}{"key": "val"}},
Arguments{Positional: []interface{}{"val"}},
}
for _, arg := range args {
assert.NoError(t, arg.Validate())
assert.False(t, len(arg.Named) > 0 && len(arg.Positional) > 0)
}
args = []Arguments{
Arguments{
Named: map[string]interface{}{"key": "val"},
Positional: []interface{}{"val"},
},
}
for _, arg := range args {
assert.Error(t, arg.Validate())
assert.True(t, len(arg.Named) > 0 && len(arg.Positional) > 0)
}
}
func TestArgumentsValidateKeys(t *testing.T) {
keys := []string{
"",
"_",
"_key",
"1",
"1key",
"\xf0\x28\x8c\xbc", // non-utf8
}
for _, key := range keys {
args := Arguments{Named: map[string]interface{}{key: "val"}}
assert.Error(t, args.Validate(), key)
}
}
func TestArgumentsUnmarshalJSON(t *testing.T) {
strs := []string{
"{}",
"{\"key\":\"val\"}",
"{\"key\":[1, 3.14, {\"key\":\"val\"}]}",
"[]",
"[\"key\",\"val\"]",
"[{}]",
}
for _, str := range strs {
var args Arguments
assert.NoError(t, json.Unmarshal([]byte(str), &args), str)
}
strs = []string{
"",
"\"",
"1",
"\"key\"",
"{\"key\",\"val\"}",
}
for _, str := range strs {
var args Arguments
assert.Error(t, json.Unmarshal([]byte(str), &args), str)
}
}
-165
View File
@@ -1,165 +0,0 @@
package dbconnect
import (
"context"
"log"
"net"
"strconv"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
)
// Cmd is the entrypoint command for dbconnect.
//
// The tunnel package is responsible for appending this to tunnel.Commands().
func Cmd() *cli.Command {
return &cli.Command{
Category: "Database Connect (ALPHA) - Deprecated",
Name: "db-connect",
Usage: "deprecated: Access your SQL database from Cloudflare Workers or the browser",
ArgsUsage: " ",
Description: `
This feature has been deprecated.
Please see:
cloudflared access tcp --help
for setting up database connections to the cloudflare edge.
Creates a connection between your database and the Cloudflare edge.
Now you can execute SQL commands anywhere you can send HTTPS requests.
Connect your database with any of the following commands, you can also try the "playground" without a database:
cloudflared db-connect --hostname sql.mysite.com --url postgres://user:pass@localhost?sslmode=disable \
--auth-domain mysite.cloudflareaccess.com --application-aud my-access-policy-tag
cloudflared db-connect --hostname sql-dev.mysite.com --url mysql://localhost --insecure
cloudflared db-connect --playground
Requests should be authenticated using Cloudflare Access, learn more about how to enable it here:
https://developers.cloudflare.com/access/service-auth/service-token/
`,
Flags: []cli.Flag{
altsrc.NewStringFlag(&cli.StringFlag{
Name: "url",
Usage: "URL to the database (eg. postgres://user:pass@localhost?sslmode=disable)",
EnvVars: []string{"TUNNEL_URL"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "hostname",
Usage: "Hostname to accept commands over HTTPS (eg. sql.mysite.com)",
EnvVars: []string{"TUNNEL_HOSTNAME"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "auth-domain",
Usage: "Cloudflare Access authentication domain for your account (eg. mysite.cloudflareaccess.com)",
EnvVars: []string{"TUNNEL_ACCESS_AUTH_DOMAIN"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "application-aud",
Usage: "Cloudflare Access application \"AUD\" to verify JWTs from requests",
EnvVars: []string{"TUNNEL_ACCESS_APPLICATION_AUD"},
}),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "insecure",
Usage: "Disable authentication, the database will be open to the Internet",
Value: false,
EnvVars: []string{"TUNNEL_ACCESS_INSECURE"},
}),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "playground",
Usage: "Run a temporary, in-memory SQLite3 database for testing",
Value: false,
EnvVars: []string{"TUNNEL_HELLO_WORLD"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "loglevel",
Value: "debug", // Make it more verbose than the tunnel default 'info'.
EnvVars: []string{"TUNNEL_LOGLEVEL"},
Hidden: true,
}),
},
Before: CmdBefore,
Action: CmdAction,
Hidden: true,
}
}
// CmdBefore runs some validation checks before running the command.
func CmdBefore(c *cli.Context) error {
// Show the help text is no flags are specified.
if c.NumFlags() == 0 {
return cli.ShowSubcommandHelp(c)
}
// Hello-world and playground are synonymous with each other,
// unset hello-world to prevent tunnel from initializing the hello package.
if c.IsSet("hello-world") {
c.Set("playground", "true")
c.Set("hello-world", "false")
}
// Unix-socket database urls are supported, but the logic is the same as url.
if c.IsSet("unix-socket") {
c.Set("url", c.String("unix-socket"))
c.Set("unix-socket", "")
}
// When playground mode is enabled, run with an in-memory database.
if c.IsSet("playground") {
c.Set("url", "sqlite3::memory:?cache=shared")
c.Set("insecure", strconv.FormatBool(!c.IsSet("auth-domain") && !c.IsSet("application-aud")))
}
// At this point, insecure configurations are valid.
if c.Bool("insecure") {
return nil
}
// Ensure that secure configurations specify a hostname, domain, and tag for JWT validation.
if !c.IsSet("hostname") || !c.IsSet("auth-domain") || !c.IsSet("application-aud") {
log.Fatal("must specify --hostname, --auth-domain, and --application-aud unless you want to run in --insecure mode")
}
return nil
}
// CmdAction starts the Proxy and sets the url in cli.Context to point to the Proxy address.
func CmdAction(c *cli.Context) error {
// STOR-612: sync with context in tunnel daemon.
ctx := context.Background()
var proxy *Proxy
var err error
if c.Bool("insecure") {
proxy, err = NewInsecureProxy(ctx, c.String("url"))
} else {
proxy, err = NewSecureProxy(ctx, c.String("url"), c.String("auth-domain"), c.String("application-aud"))
}
if err != nil {
log.Fatal(err)
return err
}
listenerC := make(chan net.Listener)
defer close(listenerC)
// Since the Proxy should only talk to the tunnel daemon, find the next available
// localhost port and start to listen to requests.
go func() {
err := proxy.Start(ctx, "127.0.0.1:", listenerC)
if err != nil {
log.Fatal(err)
}
}()
// Block until the the Proxy is online, retreive its address, and change the url to point to it.
// This is effectively "handing over" control to the tunnel package so it can run the tunnel daemon.
c.Set("url", "https://"+(<-listenerC).Addr().String())
return nil
}
-27
View File
@@ -1,27 +0,0 @@
package dbconnect
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)
func TestCmd(t *testing.T) {
tests := [][]string{
{"cloudflared", "db-connect", "--playground"},
{"cloudflared", "db-connect", "--playground", "--hostname", "sql.mysite.com"},
{"cloudflared", "db-connect", "--url", "sqlite3::memory:?cache=shared", "--insecure"},
{"cloudflared", "db-connect", "--url", "sqlite3::memory:?cache=shared", "--hostname", "sql.mysite.com", "--auth-domain", "mysite.cloudflareaccess.com", "--application-aud", "aud"},
}
app := &cli.App{
Name: "cloudflared",
Commands: []*cli.Command{Cmd()},
}
for _, test := range tests {
assert.NoError(t, app.Run(test))
}
}
-279
View File
@@ -1,279 +0,0 @@
package dbconnect
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"time"
"github.com/cloudflare/cloudflared/hello"
"github.com/cloudflare/cloudflared/validation"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/rs/zerolog"
)
const (
LogFieldCFRay = "cfRay"
)
// Proxy is an HTTP server that proxies requests to a Client.
type Proxy struct {
client Client
accessValidator *validation.Access
log *zerolog.Logger
}
// NewInsecureProxy creates a Proxy that talks to a Client at an origin.
//
// In insecure mode, the Proxy will allow all Command requests.
func NewInsecureProxy(ctx context.Context, origin string) (*Proxy, error) {
originURL, err := url.Parse(origin)
if err != nil {
return nil, errors.Wrap(err, "must provide a valid database url")
}
client, err := NewClient(ctx, originURL)
if err != nil {
return nil, err
}
err = client.Ping(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not connect to the database")
}
log := zerolog.New(os.Stderr).With().Logger() // TODO: Does not obey log configuration
return &Proxy{client, nil, &log}, nil
}
// NewSecureProxy creates a Proxy that talks to a Client at an origin.
//
// In secure mode, the Proxy will reject any Command requests that are
// not authenticated by Cloudflare Access with a valid JWT.
func NewSecureProxy(ctx context.Context, origin, authDomain, applicationAUD string) (*Proxy, error) {
proxy, err := NewInsecureProxy(ctx, origin)
if err != nil {
return nil, err
}
validator, err := validation.NewAccessValidator(ctx, authDomain, authDomain, applicationAUD)
if err != nil {
return nil, err
}
proxy.accessValidator = validator
return proxy, err
}
// IsInsecure gets whether the Proxy will accept a Command from any source.
func (proxy *Proxy) IsInsecure() bool {
return proxy.accessValidator == nil
}
// IsAllowed checks whether a http.Request is allowed to receive data.
//
// By default, requests must pass through Cloudflare Access for authentication.
// If the proxy is explcitly set to insecure mode, all requests will be allowed.
func (proxy *Proxy) IsAllowed(r *http.Request, verbose ...bool) bool {
if proxy.IsInsecure() {
return true
}
// Access and Tunnel should prevent bad JWTs from even reaching the origin,
// but validate tokens anyway as an abundance of caution.
err := proxy.accessValidator.ValidateRequest(r.Context(), r)
if err == nil {
return true
}
// Warn administrators that invalid JWTs are being rejected. This is indicative
// of either a misconfiguration of the CLI or a massive failure of upstream systems.
if len(verbose) > 0 {
proxy.log.Err(err).Str(LogFieldCFRay, proxy.getRayHeader(r)).Msg("dbproxy: Failed JWT authentication")
}
return false
}
// Start the Proxy at a given address and notify the listener channel when the server is online.
func (proxy *Proxy) Start(ctx context.Context, addr string, listenerC chan<- net.Listener) error {
// STOR-611: use a seperate listener and consider web socket support.
httpListener, err := hello.CreateTLSListener(addr)
if err != nil {
return errors.Wrapf(err, "could not create listener at %s", addr)
}
errC := make(chan error)
defer close(errC)
// Starts the HTTP server and begins to serve requests.
go func() {
errC <- proxy.httpListen(ctx, httpListener)
}()
// Continually ping the server until it comes online or 10 attempts fail.
go func() {
var err error
for i := 0; i < 10; i++ {
_, err = http.Get("http://" + httpListener.Addr().String())
// Once no error was detected, notify the listener channel and return.
if err == nil {
listenerC <- httpListener
return
}
// Backoff between requests to ping the server.
<-time.After(1 * time.Second)
}
errC <- errors.Wrap(err, "took too long for the http server to start")
}()
return <-errC
}
// httpListen starts the httpServer and blocks until the context closes.
func (proxy *Proxy) httpListen(ctx context.Context, listener net.Listener) error {
httpServer := &http.Server{
Addr: listener.Addr().String(),
Handler: proxy.httpRouter(),
ReadTimeout: 10 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
<-ctx.Done()
_ = httpServer.Close()
_ = listener.Close()
}()
return httpServer.Serve(listener)
}
// httpRouter creates a mux.Router for the Proxy.
func (proxy *Proxy) httpRouter() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/ping", proxy.httpPing()).Methods("GET", "HEAD")
router.HandleFunc("/submit", proxy.httpSubmit()).Methods("POST")
return router
}
// httpPing tests the connection to the database.
//
// By default, this endpoint is unauthenticated to allow for health checks.
// To enable authentication, Cloudflare Access must be enabled on this route.
func (proxy *Proxy) httpPing() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
err := proxy.client.Ping(ctx)
if err == nil {
proxy.httpRespond(w, r, http.StatusOK, "")
} else {
proxy.httpRespondErr(w, r, http.StatusInternalServerError, err)
}
}
}
// httpSubmit sends a command to the database and returns its response.
//
// By default, this endpoint will reject requests that do not pass through Cloudflare Access.
// To disable authentication, the --insecure flag must be specified in the command line.
func (proxy *Proxy) httpSubmit() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !proxy.IsAllowed(r, true) {
proxy.httpRespondErr(w, r, http.StatusForbidden, fmt.Errorf(""))
return
}
var cmd Command
err := json.NewDecoder(r.Body).Decode(&cmd)
if err != nil {
proxy.httpRespondErr(w, r, http.StatusBadRequest, err)
return
}
ctx := r.Context()
data, err := proxy.client.Submit(ctx, &cmd)
if err != nil {
proxy.httpRespondErr(w, r, http.StatusUnprocessableEntity, err)
return
}
w.Header().Set("Content-type", "application/json")
err = json.NewEncoder(w).Encode(data)
if err != nil {
proxy.httpRespondErr(w, r, http.StatusInternalServerError, err)
}
}
}
// httpRespond writes a status code and string response to the response writer.
func (proxy *Proxy) httpRespond(w http.ResponseWriter, r *http.Request, status int, message string) {
w.WriteHeader(status)
// Only expose the message detail of the reponse if the request is not HEAD
// and the user is authenticated. For example, this prevents an unauthenticated
// failed health check from accidentally leaking sensitive information about the Client.
if r.Method != http.MethodHead && proxy.IsAllowed(r) {
if message == "" {
message = http.StatusText(status)
}
fmt.Fprint(w, message)
}
}
// httpRespondErr is similar to httpRespond, except it formats errors to be more friendly.
func (proxy *Proxy) httpRespondErr(w http.ResponseWriter, r *http.Request, defaultStatus int, err error) {
status, err := httpError(defaultStatus, err)
proxy.httpRespond(w, r, status, err.Error())
if len(err.Error()) > 0 {
cfRay := proxy.getRayHeader(r)
proxy.log.Err(err).Str(LogFieldCFRay, cfRay).Msg("dbproxy: Database proxy error")
}
}
// getRayHeader returns the request's Cf-ray header.
func (proxy *Proxy) getRayHeader(r *http.Request) string {
return r.Header.Get("Cf-ray")
}
// httpError extracts common errors and returns an status code and friendly error.
func httpError(defaultStatus int, err error) (int, error) {
if err == nil {
return http.StatusNotImplemented, fmt.Errorf("error expected but found none")
}
if err == io.EOF {
return http.StatusBadRequest, fmt.Errorf("request body cannot be empty")
}
if err == context.DeadlineExceeded {
return http.StatusRequestTimeout, err
}
_, ok := err.(net.Error)
if ok {
return http.StatusRequestTimeout, err
}
if err == context.Canceled {
// Does not exist in Golang, but would be: http.StatusClientClosedWithoutResponse
return 444, err
}
return defaultStatus, err
}
-238
View File
@@ -1,238 +0,0 @@
package dbconnect
import (
"context"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
func TestNewInsecureProxy(t *testing.T) {
origins := []string{
"",
":/",
"http://localhost",
"tcp://localhost:9000?debug=true",
"mongodb://127.0.0.1",
}
for _, origin := range origins {
proxy, err := NewInsecureProxy(context.Background(), origin)
assert.Error(t, err)
assert.Empty(t, proxy)
}
}
func TestProxyIsAllowed(t *testing.T) {
proxy := helperNewProxy(t)
req := httptest.NewRequest("GET", "https://1.1.1.1/ping", nil)
assert.True(t, proxy.IsAllowed(req))
proxy = helperNewProxy(t, true)
req.Header.Set("Cf-access-jwt-assertion", "xxx")
assert.False(t, proxy.IsAllowed(req))
}
func TestProxyStart(t *testing.T) {
proxy := helperNewProxy(t)
ctx := context.Background()
listenerC := make(chan net.Listener)
err := proxy.Start(ctx, "1.1.1.1:", listenerC)
assert.Error(t, err)
err = proxy.Start(ctx, "127.0.0.1:-1", listenerC)
assert.Error(t, err)
ctx, cancel := context.WithTimeout(ctx, 0)
defer cancel()
err = proxy.Start(ctx, "127.0.0.1:", listenerC)
assert.IsType(t, http.ErrServerClosed, err)
}
func TestProxyHTTPRouter(t *testing.T) {
proxy := helperNewProxy(t)
router := proxy.httpRouter()
tests := []struct {
path string
method string
valid bool
}{
{"", "GET", false},
{"/", "GET", false},
{"/ping", "GET", true},
{"/ping", "HEAD", true},
{"/ping", "POST", false},
{"/submit", "POST", true},
{"/submit", "GET", false},
{"/submit/extra", "POST", false},
}
for _, test := range tests {
match := &mux.RouteMatch{}
ok := router.Match(httptest.NewRequest(test.method, "https://1.1.1.1"+test.path, nil), match)
assert.True(t, ok == test.valid, test.path)
}
}
func TestProxyHTTPPing(t *testing.T) {
proxy := helperNewProxy(t)
server := httptest.NewServer(proxy.httpPing())
defer server.Close()
client := server.Client()
res, err := client.Get(server.URL)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, int64(2), res.ContentLength)
res, err = client.Head(server.URL)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, int64(-1), res.ContentLength)
}
func TestProxyHTTPSubmit(t *testing.T) {
proxy := helperNewProxy(t)
server := httptest.NewServer(proxy.httpSubmit())
defer server.Close()
client := server.Client()
tests := []struct {
input string
status int
output string
}{
{"", http.StatusBadRequest, "request body cannot be empty"},
{"{}", http.StatusBadRequest, "cannot provide an empty statement"},
{"{\"statement\":\"Ok\"}", http.StatusUnprocessableEntity, "cannot provide invalid sql mode: ''"},
{"{\"statement\":\"Ok\",\"mode\":\"query\"}", http.StatusUnprocessableEntity, "near \"Ok\": syntax error"},
{"{\"statement\":\"CREATE TABLE t (a INT);\",\"mode\":\"exec\"}", http.StatusOK, "{\"last_insert_id\":0,\"rows_affected\":0}\n"},
}
for _, test := range tests {
res, err := client.Post(server.URL, "application/json", strings.NewReader(test.input))
assert.NoError(t, err)
assert.Equal(t, test.status, res.StatusCode)
if res.StatusCode > http.StatusOK {
assert.Equal(t, "text/plain; charset=utf-8", res.Header.Get("Content-type"))
} else {
assert.Equal(t, "application/json", res.Header.Get("Content-type"))
}
data, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
str := string(data)
assert.NoError(t, err)
assert.Equal(t, test.output, str)
}
}
func TestProxyHTTPSubmitForbidden(t *testing.T) {
proxy := helperNewProxy(t, true)
server := httptest.NewServer(proxy.httpSubmit())
defer server.Close()
client := server.Client()
res, err := client.Get(server.URL)
assert.NoError(t, err)
assert.Equal(t, http.StatusForbidden, res.StatusCode)
assert.Zero(t, res.ContentLength)
}
func TestProxyHTTPRespond(t *testing.T) {
proxy := helperNewProxy(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
proxy.httpRespond(w, r, http.StatusAccepted, "Hello")
}))
defer server.Close()
client := server.Client()
res, err := client.Get(server.URL)
assert.NoError(t, err)
assert.Equal(t, http.StatusAccepted, res.StatusCode)
assert.Equal(t, int64(5), res.ContentLength)
data, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
assert.Equal(t, []byte("Hello"), data)
}
func TestProxyHTTPRespondForbidden(t *testing.T) {
proxy := helperNewProxy(t, true)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
proxy.httpRespond(w, r, http.StatusAccepted, "Hello")
}))
defer server.Close()
client := server.Client()
res, err := client.Get(server.URL)
assert.NoError(t, err)
assert.Equal(t, http.StatusAccepted, res.StatusCode)
assert.Equal(t, int64(0), res.ContentLength)
}
func TestHTTPError(t *testing.T) {
_, errTimeout := net.DialTimeout("tcp", "127.0.0.1", 0)
assert.Error(t, errTimeout)
tests := []struct {
input error
status int
output error
}{
{nil, http.StatusNotImplemented, fmt.Errorf("error expected but found none")},
{io.EOF, http.StatusBadRequest, fmt.Errorf("request body cannot be empty")},
{context.DeadlineExceeded, http.StatusRequestTimeout, nil},
{context.Canceled, 444, nil},
{errTimeout, http.StatusRequestTimeout, nil},
{fmt.Errorf(""), http.StatusInternalServerError, nil},
}
for _, test := range tests {
status, err := httpError(http.StatusInternalServerError, test.input)
assert.Error(t, err)
assert.Equal(t, test.status, status)
if test.output == nil {
test.output = test.input
}
assert.Equal(t, test.output, err)
}
}
func helperNewProxy(t *testing.T, secure ...bool) *Proxy {
t.Helper()
proxy, err := NewSecureProxy(context.Background(), "file::memory:?cache=shared", "test.cloudflareaccess.com", "")
assert.NoError(t, err)
assert.NotNil(t, proxy)
if len(secure) == 0 {
proxy.accessValidator = nil // Mark as insecure
}
return proxy
}
-318
View File
@@ -1,318 +0,0 @@
package dbconnect
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"net/url"
"reflect"
"strings"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"github.com/xo/dburl"
// SQL drivers self-register with the database/sql package.
// https://github.com/golang/go/wiki/SQLDrivers
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"
"github.com/kshvakov/clickhouse"
"github.com/lib/pq"
)
// SQLClient is a Client that talks to a SQL database.
type SQLClient struct {
Dialect string
driver *sqlx.DB
}
// NewSQLClient creates a SQL client based on its URL scheme.
func NewSQLClient(ctx context.Context, originURL *url.URL) (Client, error) {
res, err := dburl.Parse(originURL.String())
if err != nil {
helpText := fmt.Sprintf("supported drivers: %+q, see documentation for more details: %s", sql.Drivers(), "https://godoc.org/github.com/xo/dburl")
return nil, fmt.Errorf("could not parse sql database url '%s': %s\n%s", originURL, err.Error(), helpText)
}
// Establishes the driver, but does not test the connection.
driver, err := sqlx.Open(res.Driver, res.DSN)
if err != nil {
return nil, fmt.Errorf("could not open sql driver %s: %s\n%s", res.Driver, err.Error(), res.DSN)
}
// Closes the driver, will occur when the context finishes.
go func() {
<-ctx.Done()
_ = driver.Close()
}()
return &SQLClient{driver.DriverName(), driver}, nil
}
// Ping verifies a connection to the database is still alive.
func (client *SQLClient) Ping(ctx context.Context) error {
return client.driver.PingContext(ctx)
}
// Submit queries or executes a command to the SQL database.
func (client *SQLClient) Submit(ctx context.Context, cmd *Command) (interface{}, error) {
txx, err := cmd.ValidateSQL(client.Dialect)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(ctx, cmd.Timeout)
defer cancel()
var res interface{}
// Get the next available sql.Conn and submit the Command.
err = sqlConn(ctx, client.driver, txx, func(conn *sql.Conn) error {
stmt := cmd.Statement
args := cmd.Arguments.Positional
if cmd.Mode == "query" {
res, err = sqlQuery(ctx, conn, stmt, args)
} else {
res, err = sqlExec(ctx, conn, stmt, args)
}
return err
})
return res, err
}
// ValidateSQL extends the contract of Command for SQL dialects:
// mode is conformed, arguments are []sql.NamedArg, and isolation is a sql.IsolationLevel.
//
// When the command should not be wrapped in a transaction, *sql.TxOptions and error will both be nil.
func (cmd *Command) ValidateSQL(dialect string) (*sql.TxOptions, error) {
err := cmd.Validate()
if err != nil {
return nil, err
}
mode, err := sqlMode(cmd.Mode)
if err != nil {
return nil, err
}
// Mutates Arguments to only use positional arguments with the type sql.NamedArg.
// This is a required by the sql.Driver before submitting arguments.
cmd.Arguments.sql(dialect)
iso, err := sqlIsolation(cmd.Isolation)
if err != nil {
return nil, err
}
// When isolation is out-of-range, this is indicative that no
// transaction should be executed and sql.TxOptions should be nil.
if iso < sql.LevelDefault {
return nil, nil
}
// In query mode, execute the transaction in read-only, unless it's Microsoft SQL
// which does not support that type of transaction.
readOnly := mode == "query" && dialect != "mssql"
return &sql.TxOptions{Isolation: iso, ReadOnly: readOnly}, nil
}
// sqlConn gets the next available sql.Conn in the connection pool and runs a function to use it.
//
// If the transaction options are nil, run the useIt function outside a transaction.
// This is potentially an unsafe operation if the command does not clean up its state.
func sqlConn(ctx context.Context, driver *sqlx.DB, txx *sql.TxOptions, useIt func(*sql.Conn) error) error {
conn, err := driver.Conn(ctx)
if err != nil {
return err
}
defer conn.Close()
// If transaction options are specified, begin and defer a rollback to catch errors.
var tx *sql.Tx
if txx != nil {
tx, err = conn.BeginTx(ctx, txx)
if err != nil {
return err
}
defer tx.Rollback()
}
err = useIt(conn)
// Check if useIt was successful and a transaction exists before committing.
if err == nil && tx != nil {
err = tx.Commit()
}
return err
}
// sqlQuery queries rows on a sql.Conn and returns an array of result objects.
func sqlQuery(ctx context.Context, conn *sql.Conn, stmt string, args []interface{}) ([]map[string]interface{}, error) {
rows, err := conn.QueryContext(ctx, stmt, args...)
if err == nil {
return sqlRows(rows)
}
return nil, err
}
// sqlExec executes a command on a sql.Conn and returns the result of the operation.
func sqlExec(ctx context.Context, conn *sql.Conn, stmt string, args []interface{}) (sqlResult, error) {
exec, err := conn.ExecContext(ctx, stmt, args...)
if err == nil {
return sqlResultFrom(exec), nil
}
return sqlResult{}, err
}
// sql mutates Arguments to contain a positional []sql.NamedArg.
//
// The actual return type is []interface{} due to the native Golang
// function signatures for sql.Exec and sql.Query being generic.
func (args *Arguments) sql(dialect string) {
result := args.Positional
for i, val := range result {
result[i] = sqlArg("", val, dialect)
}
for key, val := range args.Named {
result = append(result, sqlArg(key, val, dialect))
}
args.Positional = result
args.Named = map[string]interface{}{}
}
// sqlArg creates a sql.NamedArg from a key-value pair and an optional dialect.
//
// Certain dialects will need to wrap objects, such as arrays, to conform its driver requirements.
func sqlArg(key, val interface{}, dialect string) sql.NamedArg {
switch reflect.ValueOf(val).Kind() {
// PostgreSQL and Clickhouse require arrays to be wrapped before
// being inserted into the driver interface.
case reflect.Slice, reflect.Array:
switch dialect {
case "postgres":
val = pq.Array(val)
case "clickhouse":
val = clickhouse.Array(val)
}
}
return sql.Named(fmt.Sprint(key), val)
}
// sqlIsolation tries to match a string to a sql.IsolationLevel.
func sqlIsolation(str string) (sql.IsolationLevel, error) {
if str == "none" {
return sql.IsolationLevel(-1), nil
}
for iso := sql.LevelDefault; ; iso++ {
if iso > sql.LevelLinearizable {
return -1, fmt.Errorf("cannot provide an invalid sql isolation level: '%s'", str)
}
if str == "" || strings.EqualFold(iso.String(), strings.ReplaceAll(str, "_", " ")) {
return iso, nil
}
}
}
// sqlMode tries to match a string to a command mode: 'query' or 'exec' for now.
func sqlMode(str string) (string, error) {
switch str {
case "query", "exec":
return str, nil
default:
return "", fmt.Errorf("cannot provide invalid sql mode: '%s'", str)
}
}
// sqlRows scans through a SQL result set and returns an array of objects.
func sqlRows(rows *sql.Rows) ([]map[string]interface{}, error) {
columns, err := rows.Columns()
if err != nil {
return nil, errors.Wrap(err, "could not extract columns from result")
}
defer rows.Close()
types, err := rows.ColumnTypes()
if err != nil {
// Some drivers do not support type extraction, so fail silently and continue.
types = make([]*sql.ColumnType, len(columns))
}
values := make([]interface{}, len(columns))
pointers := make([]interface{}, len(columns))
var results []map[string]interface{}
for rows.Next() {
for i := range columns {
pointers[i] = &values[i]
}
_ = rows.Scan(pointers...)
// Convert a row, an array of values, into an object where
// each key is the name of its respective column.
entry := make(map[string]interface{})
for i, col := range columns {
entry[col] = sqlValue(values[i], types[i])
}
results = append(results, entry)
}
return results, nil
}
// sqlValue handles special cases where sql.Rows does not return a "human-readable" object.
func sqlValue(val interface{}, col *sql.ColumnType) interface{} {
bytes, ok := val.([]byte)
if ok {
// Opportunistically check for embeded JSON and convert it to a first-class object.
var embeded interface{}
if json.Unmarshal(bytes, &embeded) == nil {
return embeded
}
// STOR-604: investigate a way to coerce PostgreSQL arrays '{a, b, ...}' into JSON.
// Although easy with strings, it becomes more difficult with special types like INET[].
return string(bytes)
}
return val
}
// sqlResult is a thin wrapper around sql.Result.
type sqlResult struct {
LastInsertId int64 `json:"last_insert_id"`
RowsAffected int64 `json:"rows_affected"`
}
// sqlResultFrom converts sql.Result into a JSON-marshable sqlResult.
func sqlResultFrom(res sql.Result) sqlResult {
insertID, errID := res.LastInsertId()
rowsAffected, errRows := res.RowsAffected()
// If an error occurs when extracting the result, it is because the
// driver does not support that specific field. Instead of passing this
// to the user, omit the field in the response.
if errID != nil {
insertID = -1
}
if errRows != nil {
rowsAffected = -1
}
return sqlResult{insertID, rowsAffected}
}
-336
View File
@@ -1,336 +0,0 @@
package dbconnect
import (
"context"
"database/sql"
"fmt"
"net/url"
"strings"
"testing"
"time"
"github.com/kshvakov/clickhouse"
"github.com/lib/pq"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)
func TestNewSQLClient(t *testing.T) {
originURLs := []string{
"postgres://localhost",
"cockroachdb://localhost:1337",
"postgresql://user:pass@127.0.0.1",
"mysql://localhost",
"clickhouse://127.0.0.1:9000/?debug",
"sqlite3::memory:",
"file:test.db?cache=shared",
}
for _, originURL := range originURLs {
origin, _ := url.Parse(originURL)
_, err := NewSQLClient(context.Background(), origin)
assert.NoError(t, err, originURL)
}
originURLs = []string{
"",
"/",
"http://localhost",
"coolthing://user:pass@127.0.0.1",
}
for _, originURL := range originURLs {
origin, _ := url.Parse(originURL)
_, err := NewSQLClient(context.Background(), origin)
assert.Error(t, err, originURL)
}
}
func TestArgumentsSQL(t *testing.T) {
args := []Arguments{
Arguments{
Positional: []interface{}{
"val", 10, 3.14,
},
},
Arguments{
Named: map[string]interface{}{
"key": time.Unix(0, 0),
},
},
}
var nameType sql.NamedArg
for _, arg := range args {
arg.sql("")
for _, named := range arg.Positional {
assert.IsType(t, nameType, named)
}
}
}
func TestSQLArg(t *testing.T) {
tests := []struct {
key interface{}
val interface{}
dialect string
arg sql.NamedArg
}{
{"key", "val", "mssql", sql.Named("key", "val")},
{0, 1, "sqlite3", sql.Named("0", 1)},
{1, []string{"a", "b", "c"}, "postgres", sql.Named("1", pq.Array([]string{"a", "b", "c"}))},
{"in", []uint{0, 1}, "clickhouse", sql.Named("in", clickhouse.Array([]uint{0, 1}))},
{"", time.Unix(0, 0), "", sql.Named("", time.Unix(0, 0))},
}
for _, test := range tests {
arg := sqlArg(test.key, test.val, test.dialect)
assert.Equal(t, test.arg, arg, test.key)
}
}
func TestSQLIsolation(t *testing.T) {
tests := []struct {
str string
iso sql.IsolationLevel
}{
{"", sql.LevelDefault},
{"DEFAULT", sql.LevelDefault},
{"read_UNcommitted", sql.LevelReadUncommitted},
{"serializable", sql.LevelSerializable},
{"none", sql.IsolationLevel(-1)},
{"SNAP shot", -2},
{"blah", -2},
}
for _, test := range tests {
iso, err := sqlIsolation(test.str)
if test.iso < -1 {
assert.Error(t, err, test.str)
} else {
assert.NoError(t, err)
assert.Equal(t, test.iso, iso, test.str)
}
}
}
func TestSQLMode(t *testing.T) {
modes := []string{
"query",
"exec",
}
for _, mode := range modes {
actual, err := sqlMode(mode)
assert.NoError(t, err)
assert.Equal(t, strings.ToLower(mode), actual, mode)
}
modes = []string{
"",
"blah",
}
for _, mode := range modes {
_, err := sqlMode(mode)
assert.Error(t, err)
}
}
func helperRows(mockRows *sqlmock.Rows) *sql.Rows {
db, mock, _ := sqlmock.New()
mock.ExpectQuery("SELECT").WillReturnRows(mockRows)
rows, _ := db.Query("SELECT")
return rows
}
func TestSQLRows(t *testing.T) {
actual, err := sqlRows(helperRows(sqlmock.NewRows(
[]string{"name", "age", "dept"}).
AddRow("alice", 19, "prod")))
expected := []map[string]interface{}{map[string]interface{}{
"name": "alice",
"age": int64(19),
"dept": "prod"}}
assert.NoError(t, err)
assert.ElementsMatch(t, expected, actual)
}
func TestSQLValue(t *testing.T) {
tests := []struct {
input interface{}
output interface{}
}{
{"hello", "hello"},
{1, 1},
{false, false},
{[]byte("random"), "random"},
{[]byte("{\"json\":true}"), map[string]interface{}{"json": true}},
{[]byte("[]"), []interface{}{}},
}
for _, test := range tests {
assert.Equal(t, test.output, sqlValue(test.input, nil), test.input)
}
}
func TestSQLResultFrom(t *testing.T) {
res := sqlResultFrom(sqlmock.NewResult(1, 2))
assert.Equal(t, sqlResult{1, 2}, res)
res = sqlResultFrom(sqlmock.NewErrorResult(fmt.Errorf("")))
assert.Equal(t, sqlResult{-1, -1}, res)
}
func helperSQLite3(t *testing.T) (context.Context, Client) {
t.Helper()
ctx := context.Background()
url, _ := url.Parse("file::memory:?cache=shared")
sqlite3, err := NewSQLClient(ctx, url)
assert.NoError(t, err)
return ctx, sqlite3
}
func TestPing(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
err := sqlite3.Ping(ctx)
assert.NoError(t, err)
}
func TestSubmit(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
res, err := sqlite3.Submit(ctx, &Command{
Statement: "CREATE TABLE t (a INTEGER, b FLOAT, c TEXT, d BLOB);",
Mode: "exec",
})
assert.NoError(t, err)
assert.Equal(t, sqlResult{0, 0}, res)
res, err = sqlite3.Submit(ctx, &Command{
Statement: "SELECT * FROM t;",
Mode: "query",
})
assert.NoError(t, err)
assert.Empty(t, res)
res, err = sqlite3.Submit(ctx, &Command{
Statement: "INSERT INTO t VALUES (?, ?, ?, ?);",
Mode: "exec",
Arguments: Arguments{
Positional: []interface{}{
1,
3.14,
"text",
"blob",
},
},
})
assert.NoError(t, err)
assert.Equal(t, sqlResult{1, 1}, res)
res, err = sqlite3.Submit(ctx, &Command{
Statement: "UPDATE t SET c = NULL;",
Mode: "exec",
})
assert.NoError(t, err)
assert.Equal(t, sqlResult{1, 1}, res)
res, err = sqlite3.Submit(ctx, &Command{
Statement: "SELECT * FROM t WHERE a = ?;",
Mode: "query",
Arguments: Arguments{
Positional: []interface{}{1},
},
})
assert.NoError(t, err)
assert.Len(t, res, 1)
resf, ok := res.([]map[string]interface{})
assert.True(t, ok)
assert.EqualValues(t, map[string]interface{}{
"a": int64(1),
"b": 3.14,
"c": nil,
"d": "blob",
}, resf[0])
res, err = sqlite3.Submit(ctx, &Command{
Statement: "DROP TABLE t;",
Mode: "exec",
})
assert.NoError(t, err)
assert.Equal(t, sqlResult{1, 1}, res)
}
func TestSubmitTransaction(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
res, err := sqlite3.Submit(ctx, &Command{
Statement: "BEGIN;",
Mode: "exec",
})
assert.Error(t, err)
assert.Empty(t, res)
res, err = sqlite3.Submit(ctx, &Command{
Statement: "BEGIN; CREATE TABLE tt (a INT); COMMIT;",
Mode: "exec",
Isolation: "none",
})
assert.NoError(t, err)
assert.Equal(t, sqlResult{0, 0}, res)
rows, err := sqlite3.Submit(ctx, &Command{
Statement: "SELECT * FROM tt;",
Mode: "query",
Isolation: "repeatable_read",
})
assert.NoError(t, err)
assert.Empty(t, rows)
}
func TestSubmitTimeout(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
res, err := sqlite3.Submit(ctx, &Command{
Statement: "SELECT * FROM t;",
Mode: "query",
Timeout: 1 * time.Nanosecond,
})
assert.Error(t, err)
assert.Empty(t, res)
}
func TestSubmitMode(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
res, err := sqlite3.Submit(ctx, &Command{
Statement: "SELECT * FROM t;",
Mode: "notanoption",
})
assert.Error(t, err)
assert.Empty(t, res)
}
func TestSubmitEmpty(t *testing.T) {
ctx, sqlite3 := helperSQLite3(t)
res, err := sqlite3.Submit(ctx, &Command{
Statement: "; ; ; ;",
Mode: "query",
})
assert.Error(t, err)
assert.Empty(t, res)
}
-78
View File
@@ -1,78 +0,0 @@
# docker-compose -f ./dbconnect_tests/dbconnect.yaml up --build --force-recreate --renew-anon-volumes --exit-code-from cloudflared
version: "2.3"
networks:
test-dbconnect-network:
driver: bridge
services:
cloudflared:
build:
context: ../
dockerfile: dev.Dockerfile
command: go test github.com/cloudflare/cloudflared/dbconnect_tests -v
depends_on:
postgres:
condition: service_healthy
mysql:
condition: service_healthy
mssql:
condition: service_healthy
clickhouse:
condition: service_healthy
environment:
DBCONNECT_INTEGRATION_TEST: "true"
POSTGRESQL_URL: postgres://postgres:secret@postgres/db?sslmode=disable
MYSQL_URL: mysql://root:secret@mysql/db?tls=false
MSSQL_URL: mssql://sa:secret12345!@mssql
CLICKHOUSE_URL: clickhouse://clickhouse:9000/db
networks:
- test-dbconnect-network
postgres:
image: postgres:11.4-alpine
environment:
POSTGRES_DB: db
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
start_period: 3s
interval: 1s
timeout: 3s
retries: 10
networks:
- test-dbconnect-network
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: db
MYSQL_ROOT_PASSWORD: secret
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
start_period: 3s
interval: 1s
timeout: 3s
retries: 10
networks:
- test-dbconnect-network
mssql:
image: mcr.microsoft.com/mssql/server:2017-CU8-ubuntu
environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: secret12345!
healthcheck:
test: ["CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "secret12345!", "-Q", "SELECT 1"]
start_period: 3s
interval: 1s
timeout: 3s
retries: 10
networks:
- test-dbconnect-network
clickhouse:
image: yandex/clickhouse-server:19.11
healthcheck:
test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
start_period: 3s
interval: 1s
timeout: 3s
retries: 10
networks:
- test-dbconnect-network
-265
View File
@@ -1,265 +0,0 @@
package dbconnect_tests
import (
"context"
"log"
"net/url"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/cloudflare/cloudflared/dbconnect"
)
func TestIntegrationPostgreSQL(t *testing.T) {
ctx, pq := helperNewSQLClient(t, "POSTGRESQL_URL")
err := pq.Ping(ctx)
assert.NoError(t, err)
_, err = pq.Submit(ctx, &dbconnect.Command{
Statement: "CREATE TABLE t (a TEXT, b UUID, c JSON, d INET[], e SERIAL);",
Mode: "exec",
})
assert.NoError(t, err)
_, err = pq.Submit(ctx, &dbconnect.Command{
Statement: "INSERT INTO t VALUES ($1, $2, $3, $4);",
Mode: "exec",
Arguments: dbconnect.Arguments{
Positional: []interface{}{
"text",
"6b8d686d-bd8e-43bc-b09a-cfcbbe702c10",
"{\"bool\":true,\"array\":[\"a\", 1, 3.14],\"embed\":{\"num\":21}}",
[]string{"1.1.1.1", "1.0.0.1"},
},
},
})
assert.NoError(t, err)
_, err = pq.Submit(ctx, &dbconnect.Command{
Statement: "UPDATE t SET b = NULL;",
Mode: "exec",
})
assert.NoError(t, err)
res, err := pq.Submit(ctx, &dbconnect.Command{
Statement: "SELECT * FROM t;",
Mode: "query",
})
assert.NoError(t, err)
assert.IsType(t, make([]map[string]interface{}, 0), res)
actual := res.([]map[string]interface{})[0]
expected := map[string]interface{}{
"a": "text",
"b": nil,
"c": map[string]interface{}{
"bool": true,
"array": []interface{}{"a", float64(1), 3.14},
"embed": map[string]interface{}{"num": float64(21)},
},
"d": "{1.1.1.1,1.0.0.1}",
"e": int64(1),
}
assert.EqualValues(t, expected, actual)
_, err = pq.Submit(ctx, &dbconnect.Command{
Statement: "DROP TABLE t;",
Mode: "exec",
})
assert.NoError(t, err)
}
func TestIntegrationMySQL(t *testing.T) {
ctx, my := helperNewSQLClient(t, "MYSQL_URL")
err := my.Ping(ctx)
assert.NoError(t, err)
_, err = my.Submit(ctx, &dbconnect.Command{
Statement: "CREATE TABLE t (a CHAR, b TINYINT, c FLOAT, d JSON, e YEAR);",
Mode: "exec",
})
assert.NoError(t, err)
_, err = my.Submit(ctx, &dbconnect.Command{
Statement: "INSERT INTO t VALUES (?, ?, ?, ?, ?);",
Mode: "exec",
Arguments: dbconnect.Arguments{
Positional: []interface{}{
"a",
10,
3.14,
"{\"bool\":true}",
2000,
},
},
})
assert.NoError(t, err)
_, err = my.Submit(ctx, &dbconnect.Command{
Statement: "ALTER TABLE t ADD COLUMN f GEOMETRY;",
Mode: "exec",
})
assert.NoError(t, err)
res, err := my.Submit(ctx, &dbconnect.Command{
Statement: "SELECT * FROM t;",
Mode: "query",
})
assert.NoError(t, err)
assert.IsType(t, make([]map[string]interface{}, 0), res)
actual := res.([]map[string]interface{})[0]
expected := map[string]interface{}{
"a": "a",
"b": float64(10),
"c": 3.14,
"d": map[string]interface{}{"bool": true},
"e": float64(2000),
"f": nil,
}
assert.EqualValues(t, expected, actual)
_, err = my.Submit(ctx, &dbconnect.Command{
Statement: "DROP TABLE t;",
Mode: "exec",
})
assert.NoError(t, err)
}
func TestIntegrationMSSQL(t *testing.T) {
ctx, ms := helperNewSQLClient(t, "MSSQL_URL")
err := ms.Ping(ctx)
assert.NoError(t, err)
_, err = ms.Submit(ctx, &dbconnect.Command{
Statement: "CREATE TABLE t (a BIT, b DECIMAL, c MONEY, d TEXT);",
Mode: "exec"})
assert.NoError(t, err)
_, err = ms.Submit(ctx, &dbconnect.Command{
Statement: "INSERT INTO t VALUES (?, ?, ?, ?);",
Mode: "exec",
Arguments: dbconnect.Arguments{
Positional: []interface{}{
0,
3,
"$0.99",
"text",
},
},
})
assert.NoError(t, err)
_, err = ms.Submit(ctx, &dbconnect.Command{
Statement: "UPDATE t SET d = NULL;",
Mode: "exec",
})
assert.NoError(t, err)
res, err := ms.Submit(ctx, &dbconnect.Command{
Statement: "SELECT * FROM t;",
Mode: "query",
})
assert.NoError(t, err)
assert.IsType(t, make([]map[string]interface{}, 0), res)
actual := res.([]map[string]interface{})[0]
expected := map[string]interface{}{
"a": false,
"b": float64(3),
"c": float64(0.99),
"d": nil,
}
assert.EqualValues(t, expected, actual)
_, err = ms.Submit(ctx, &dbconnect.Command{
Statement: "DROP TABLE t;",
Mode: "exec",
})
assert.NoError(t, err)
}
func TestIntegrationClickhouse(t *testing.T) {
ctx, ch := helperNewSQLClient(t, "CLICKHOUSE_URL")
err := ch.Ping(ctx)
assert.NoError(t, err)
_, err = ch.Submit(ctx, &dbconnect.Command{
Statement: "CREATE TABLE t (a UUID, b String, c Float64, d UInt32, e Int16, f Array(Enum8('a'=1, 'b'=2, 'c'=3))) engine=Memory;",
Mode: "exec",
})
assert.NoError(t, err)
_, err = ch.Submit(ctx, &dbconnect.Command{
Statement: "INSERT INTO t VALUES (?, ?, ?, ?, ?, ?);",
Mode: "exec",
Arguments: dbconnect.Arguments{
Positional: []interface{}{
"ec65f626-6f50-4c86-9628-6314ef1edacd",
"",
3.14,
314,
-144,
[]string{"a", "b", "c"},
},
},
})
assert.NoError(t, err)
res, err := ch.Submit(ctx, &dbconnect.Command{
Statement: "SELECT * FROM t;",
Mode: "query",
})
assert.NoError(t, err)
assert.IsType(t, make([]map[string]interface{}, 0), res)
actual := res.([]map[string]interface{})[0]
expected := map[string]interface{}{
"a": "ec65f626-6f50-4c86-9628-6314ef1edacd",
"b": "",
"c": float64(3.14),
"d": uint32(314),
"e": int16(-144),
"f": []string{"a", "b", "c"},
}
assert.EqualValues(t, expected, actual)
_, err = ch.Submit(ctx, &dbconnect.Command{
Statement: "DROP TABLE t;",
Mode: "exec",
})
assert.NoError(t, err)
}
func helperNewSQLClient(t *testing.T, env string) (context.Context, dbconnect.Client) {
_, ok := os.LookupEnv("DBCONNECT_INTEGRATION_TEST")
if ok {
t.Helper()
} else {
t.SkipNow()
}
val, ok := os.LookupEnv(env)
if !ok {
log.Fatalf("must provide database url as environment variable: %s", env)
}
parsed, err := url.Parse(val)
if err != nil {
log.Fatalf("cannot provide invalid database url: %s=%s", env, val)
}
ctx := context.Background()
client, err := dbconnect.NewSQLClient(ctx, parsed)
if err != nil {
log.Fatalf("could not start test client: %s", err)
}
return ctx, client
}
-15
View File
@@ -3,29 +3,20 @@ module github.com/cloudflare/cloudflared
go 1.15
require (
github.com/DATA-DOG/go-sqlmock v1.3.3
github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 // indirect
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/aws/aws-sdk-go v1.34.19 // indirect
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc
github.com/coredns/coredns v1.7.0
github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect
github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/frankban/quicktest v1.10.0 // indirect
github.com/fsnotify/fsnotify v1.4.9
github.com/gdamore/tcell v1.3.0
github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10
github.com/gliderlabs/ssh v0.0.0-20191009160644-63518b5243e0 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.0.4
@@ -34,19 +25,14 @@ require (
github.com/google/uuid v1.1.2
github.com/gorilla/mux v1.7.3
github.com/gorilla/websocket v1.4.2
github.com/jmoiron/sqlx v1.2.0
github.com/json-iterator/go v1.1.10
github.com/kr/text v0.2.0 // indirect
github.com/kshvakov/clickhouse v1.3.11
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lib/pq v1.2.0
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-sqlite3 v1.11.0
github.com/miekg/dns v1.1.31
github.com/mitchellh/go-homedir v1.1.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_golang v1.7.1
@@ -55,7 +41,6 @@ require (
github.com/rs/zerolog v1.20.0
github.com/stretchr/testify v1.6.0
github.com/urfave/cli/v2 v2.2.0
github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/net v0.0.0-20200904194848-62affa334b73
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
-34
View File
@@ -75,8 +75,6 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
github.com/Shopify/sarama v1.21.0/go.mod h1:yuqtN/pe8cXRWG5zPaO7hCfNJp5MwmkoJEoLjkm5tCQ=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 h1:RKnVV4C7qoN/sToLX2y1dqH7T6kKLMHcwRJlgwb9Ggk=
github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1/go.mod h1:gI5CyA/CEnS6eqNV22rqs4dG3aGfaSbXgPORIlwr2r0=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
@@ -88,8 +86,6 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2c
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -100,7 +96,6 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.32.1/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.34.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -108,8 +103,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/caddyserver/caddy v1.0.5 h1:5B1Hs0UF2x2tggr2X9jL2qOZtDXbIWQb9YLbmlxHSuM=
github.com/caddyserver/caddy v1.0.5/go.mod h1:AnFHB+/MrgRC+mJAvuAgQ38ePzw+wKeW0wzENpdQQKY=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@@ -133,8 +126,6 @@ github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93/go.mod h1:QiT
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc h1:Dvk3ySBsOm5EviLx6VCyILnafPcQinXGP5jbTdHUJgE=
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc/go.mod h1:HlgKKR8V5a1wroIDDIz3/A+T+9Janfq+7n1P5sEFdi0=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
@@ -159,8 +150,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosEb9uq4Sy+08g=
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0 h1:epsH3lb7KVbXHYk7LYGN5EiE0MxcevHU85CKITJ0wUY=
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
@@ -199,8 +188,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -212,8 +199,6 @@ github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 h1:YO10pIIBftO/
github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.0.0-20191009160644-63518b5243e0 h1:gF8ngtda767ddth2SH0YSAhswhz6qUkvyI9EZFYCWJA=
github.com/gliderlabs/ssh v0.0.0-20191009160644-63518b5243e0/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
github.com/go-acme/lego/v3 v3.2.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
@@ -253,8 +238,6 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -371,8 +354,6 @@ github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mo
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -403,16 +384,11 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kshvakov/clickhouse v1.3.11 h1:dtzTJY0fCA+MWkLyuKZaNPkmSwdX4gh8+Klic9NB1Lw=
github.com/kshvakov/clickhouse v1.3.11/go.mod h1:/SVBAcqF3u7rxQ9sTWCZwf8jzzvxiZGeQvtmSF2BBEc=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
@@ -437,9 +413,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -519,8 +492,6 @@ github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -626,8 +597,6 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0 h1:6DtWz8hNS4qbq0OCRPhdBMG9E2qKTSDKlwnP3dmZvuA=
github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0/go.mod h1:A47W3pdWONaZmXuLZgfKLAVgUY0qvfTRM5vVDKS40S4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -657,7 +626,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -815,8 +783,6 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
+10 -4
View File
@@ -2,6 +2,7 @@ package origin
import (
"context"
"math/rand"
"time"
)
@@ -30,7 +31,7 @@ type BackoffHandler struct {
resetDeadline time.Time
}
func (b BackoffHandler) GetBackoffDuration(ctx context.Context) (time.Duration, bool) {
func (b BackoffHandler) GetMaxBackoffDuration(ctx context.Context) (time.Duration, bool) {
// Follows the same logic as Backoff, but without mutating the receiver.
// This select has to happen first to reflect the actual behaviour of the Backoff function.
select {
@@ -45,7 +46,8 @@ func (b BackoffHandler) GetBackoffDuration(ctx context.Context) (time.Duration,
if b.retries >= b.MaxRetries && !b.RetryForever {
return time.Duration(0), false
}
return time.Duration(b.GetBaseTime() * 1 << b.retries), true
maxTimeToWait := b.GetBaseTime() * 1 << (b.retries + 1)
return maxTimeToWait, true
}
// BackoffTimer returns a channel that sends the current time when the exponential backoff timeout expires.
@@ -62,7 +64,9 @@ func (b *BackoffHandler) BackoffTimer() <-chan time.Time {
} else {
b.retries++
}
return timeAfter(time.Duration(b.GetBaseTime() * 1 << (b.retries - 1)))
maxTimeToWait := time.Duration(b.GetBaseTime() * 1 << (b.retries))
timeToWait := time.Duration(rand.Int63n(maxTimeToWait.Nanoseconds()))
return timeAfter(timeToWait)
}
// Backoff is used to wait according to exponential backoff. Returns false if the
@@ -83,7 +87,9 @@ func (b *BackoffHandler) Backoff(ctx context.Context) bool {
// Sets a grace period within which the the backoff timer is maintained. After the grace
// period expires, the number of retries & backoff duration is reset.
func (b *BackoffHandler) SetGracePeriod() {
b.resetDeadline = timeNow().Add(time.Duration(b.GetBaseTime() * 2 << b.retries))
maxTimeToWait := b.GetBaseTime() * 2 << (b.retries + 1)
timeToWait := time.Duration(rand.Int63n(maxTimeToWait.Nanoseconds()))
b.resetDeadline = timeNow().Add(timeToWait)
}
func (b BackoffHandler) GetBaseTime() time.Duration {
+23 -23
View File
@@ -40,7 +40,7 @@ func TestBackoffCancel(t *testing.T) {
if backoff.Backoff(ctx) {
t.Fatalf("backoff allowed after cancel")
}
if _, ok := backoff.GetBackoffDuration(ctx); ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); ok {
t.Fatalf("backoff allowed after cancel")
}
}
@@ -69,24 +69,24 @@ func TestBackoffGracePeriod(t *testing.T) {
}
}
func TestGetBackoffDurationRetries(t *testing.T) {
func TestGetMaxBackoffDurationRetries(t *testing.T) {
// make backoff return immediately
timeAfter = immediateTimeAfter
ctx := context.Background()
backoff := BackoffHandler{MaxRetries: 3}
if _, ok := backoff.GetBackoffDuration(ctx); !ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
t.Fatalf("backoff failed immediately")
}
backoff.Backoff(ctx) // noop
if _, ok := backoff.GetBackoffDuration(ctx); !ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
t.Fatalf("backoff failed after 1 retry")
}
backoff.Backoff(ctx) // noop
if _, ok := backoff.GetBackoffDuration(ctx); !ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
t.Fatalf("backoff failed after 2 retry")
}
backoff.Backoff(ctx) // noop
if _, ok := backoff.GetBackoffDuration(ctx); ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); ok {
t.Fatalf("backoff allowed after 3 (max) retries")
}
if backoff.Backoff(ctx) {
@@ -94,25 +94,25 @@ func TestGetBackoffDurationRetries(t *testing.T) {
}
}
func TestGetBackoffDuration(t *testing.T) {
func TestGetMaxBackoffDuration(t *testing.T) {
// make backoff return immediately
timeAfter = immediateTimeAfter
ctx := context.Background()
backoff := BackoffHandler{MaxRetries: 3}
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second {
t.Fatalf("backoff didn't return 1 second on first retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*2 {
t.Fatalf("backoff (%s) didn't return < 2 seconds on first retry", duration)
}
backoff.Backoff(ctx) // noop
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*2 {
t.Fatalf("backoff didn't return 2 seconds on second retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*4 {
t.Fatalf("backoff (%s) didn't return < 4 seconds on second retry", duration)
}
backoff.Backoff(ctx) // noop
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*4 {
t.Fatalf("backoff didn't return 4 seconds on third retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*8 {
t.Fatalf("backoff (%s) didn't return < 8 seconds on third retry", duration)
}
backoff.Backoff(ctx) // noop
if duration, ok := backoff.GetBackoffDuration(ctx); ok || duration != 0 {
t.Fatalf("backoff didn't return 0 seconds on fourth retry (exceeding limit)")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); ok || duration != 0 {
t.Fatalf("backoff (%s) didn't return 0 seconds on fourth retry (exceeding limit)", duration)
}
}
@@ -121,27 +121,27 @@ func TestBackoffRetryForever(t *testing.T) {
timeAfter = immediateTimeAfter
ctx := context.Background()
backoff := BackoffHandler{MaxRetries: 3, RetryForever: true}
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second {
t.Fatalf("backoff didn't return 1 second on first retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*2 {
t.Fatalf("backoff (%s) didn't return < 2 seconds on first retry", duration)
}
backoff.Backoff(ctx) // noop
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*2 {
t.Fatalf("backoff didn't return 2 seconds on second retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*4 {
t.Fatalf("backoff (%s) didn't return < 4 seconds on second retry", duration)
}
backoff.Backoff(ctx) // noop
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*4 {
t.Fatalf("backoff didn't return 4 seconds on third retry")
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*8 {
t.Fatalf("backoff (%s) didn't return < 8 seconds on third retry", duration)
}
if !backoff.Backoff(ctx) {
t.Fatalf("backoff refused on fourth retry despire RetryForever")
}
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*8 {
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*16 {
t.Fatalf("backoff returned %v instead of 8 seconds on fourth retry", duration)
}
if !backoff.Backoff(ctx) {
t.Fatalf("backoff refused on fifth retry despire RetryForever")
}
if duration, ok := backoff.GetBackoffDuration(ctx); !ok || duration != time.Second*8 {
if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*16 {
t.Fatalf("backoff returned %v instead of 8 seconds on fifth retry", duration)
}
}
+1 -1
View File
@@ -108,7 +108,7 @@ func (cm *reconnectCredentialManager) RefreshAuth(
authOutcome, err := authenticate(ctx, backoff.Retries())
if err != nil {
cm.authFail.WithLabelValues(err.Error()).Inc()
if _, ok := backoff.GetBackoffDuration(ctx); ok {
if _, ok := backoff.GetMaxBackoffDuration(ctx); ok {
return backoff.BackoffTimer(), nil
}
return nil, err
+9 -7
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
)
@@ -28,13 +29,14 @@ func TestRefreshAuthBackoff(t *testing.T) {
// authentication failures should consume the backoff
for i := uint(0); i < backoff.MaxRetries; i++ {
retryChan, err := rcm.RefreshAuth(context.Background(), backoff, auth)
assert.NoError(t, err)
assert.NotNil(t, retryChan)
assert.Equal(t, (1<<i)*time.Second, wait)
require.NoError(t, err)
require.NotNil(t, retryChan)
require.Greater(t, wait.Seconds(), 0.0)
require.Less(t, wait.Seconds(), float64((1<<(i+1))*time.Second))
}
retryChan, err := rcm.RefreshAuth(context.Background(), backoff, auth)
assert.Error(t, err)
assert.Nil(t, retryChan)
require.Error(t, err)
require.Nil(t, retryChan)
// now we actually make contact with the remote server
_, _ = rcm.RefreshAuth(context.Background(), backoff, func(ctx context.Context, n int) (tunnelpogs.AuthOutcome, error) {
@@ -47,8 +49,8 @@ func TestRefreshAuthBackoff(t *testing.T) {
expectedGracePeriod := time.Duration(time.Second * 2 << backoff.MaxRetries)
return time.Now().Add(expectedGracePeriod * 2)
}
_, ok := backoff.GetBackoffDuration(context.Background())
assert.True(t, ok)
_, ok := backoff.GetMaxBackoffDuration(context.Background())
require.True(t, ok)
}
func TestRefreshAuthSuccess(t *testing.T) {
+12 -10
View File
@@ -174,11 +174,11 @@ func ServeTunnelLoop(
config.Observer.SendReconnect(connIndex)
duration, ok := protocolFallback.GetBackoffDuration(ctx)
duration, ok := protocolFallback.GetMaxBackoffDuration(ctx)
if !ok {
return err
}
connLog.Info().Msgf("Retrying connection in %s seconds", duration)
connLog.Info().Msgf("Retrying connection in up to %s seconds", duration)
select {
case <-ctx.Done():
@@ -244,7 +244,7 @@ func selectNextProtocol(
// on error returns a flag indicating if error can be retried
func ServeTunnel(
ctx context.Context,
connLong *zerolog.Logger,
connLog *zerolog.Logger,
credentialManager *reconnectCredentialManager,
config *TunnelConfig,
addr *net.TCPAddr,
@@ -273,6 +273,7 @@ func ServeTunnel(
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr)
if err != nil {
connLog.Err(err).Msg("Unable to establish connection with Cloudflare edge")
return err, true
}
connectedFuse := &connectedFuse{
@@ -284,7 +285,7 @@ func ServeTunnel(
connOptions := config.ConnectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.retries))
err = ServeHTTP2(
ctx,
connLong,
connLog,
config,
edgeConn,
connOptions,
@@ -296,7 +297,7 @@ func ServeTunnel(
} else {
err = ServeH2mux(
ctx,
connLong,
connLog,
credentialManager,
config,
edgeConn,
@@ -311,28 +312,29 @@ func ServeTunnel(
if err != nil {
switch err := err.(type) {
case connection.DupConnRegisterTunnelError:
connLog.Err(err).Msg("Unable to establish connection.")
// don't retry this connection anymore, let supervisor pick a new address
return err, false
case connection.ServerRegisterTunnelError:
connLong.Err(err).Msg("Register tunnel error from server side")
connLog.Err(err).Msg("Register tunnel error from server side")
// Don't send registration error return from server to Sentry. They are
// logged on server side
if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
connLong.Error().Msg(activeIncidentsMsg(incidents))
connLog.Error().Msg(activeIncidentsMsg(incidents))
}
return err.Cause, !err.Permanent
case ReconnectSignal:
connLong.Info().
connLog.Info().
Uint8(connection.LogFieldConnIndex, connIndex).
Msgf("Restarting connection due to reconnect signal in %s", err.Delay)
err.DelayBeforeReconnect()
return err, true
default:
if err == context.Canceled {
connLong.Debug().Err(err).Msgf("Serve tunnel error")
connLog.Debug().Err(err).Msgf("Serve tunnel error")
return err, false
}
connLong.Err(err).Msgf("Serve tunnel error")
connLog.Err(err).Msgf("Serve tunnel error")
_, permanent := err.(unrecoverableError)
return err, !permanent
}
+5 -4
View File
@@ -30,12 +30,12 @@ type UpstreamHTTPS struct {
}
// NewUpstreamHTTPS creates a new DNS over HTTPS upstream from endpoint
func NewUpstreamHTTPS(endpoint string, bootstraps []string, log *zerolog.Logger) (Upstream, error) {
func NewUpstreamHTTPS(endpoint string, bootstraps []string, maxConnections int, log *zerolog.Logger) (Upstream, error) {
u, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
return &UpstreamHTTPS{client: configureClient(u.Hostname()), endpoint: u, bootstraps: bootstraps, log: log}, nil
return &UpstreamHTTPS{client: configureClient(u.Hostname(), maxConnections), endpoint: u, bootstraps: bootstraps, log: log}, nil
}
// Exchange provides an implementation for the Upstream interface
@@ -122,17 +122,18 @@ func configureBootstrap(bootstrap string) (*url.URL, *http.Client, error) {
return nil, nil, fmt.Errorf("bootstrap address of %s must be an IP address", b.Hostname())
}
return b, configureClient(b.Hostname()), nil
return b, configureClient(b.Hostname(), MaxUpstreamConnsDefault), nil
}
// configureClient will configure a HTTPS client for upstream DoH requests
func configureClient(hostname string) *http.Client {
func configureClient(hostname string, maxUpstreamConnections int) *http.Client {
// Update TLS and HTTP client configuration
tlsConfig := &tls.Config{ServerName: hostname}
transport := &http.Transport{
TLSClientConfig: tlsConfig,
DisableCompression: true,
MaxIdleConns: 1,
MaxConnsPerHost: maxUpstreamConnections,
Proxy: http.ProxyFromEnvironment,
}
_ = http2.ConfigureTransport(transport)
+13 -4
View File
@@ -21,8 +21,9 @@ import (
)
const (
LogFieldAddress = "address"
LogFieldURL = "url"
LogFieldAddress = "address"
LogFieldURL = "url"
MaxUpstreamConnsDefault = 5
)
// Listener is an adapter between CoreDNS server and Warp runnable
@@ -69,6 +70,12 @@ func Command(hidden bool) *cli.Command {
Value: cli.NewStringSlice("https://162.159.36.1/dns-query", "https://162.159.46.1/dns-query", "https://[2606:4700:4700::1111]/dns-query", "https://[2606:4700:4700::1001]/dns-query"),
EnvVars: []string{"TUNNEL_DNS_BOOTSTRAP"},
},
&cli.IntFlag{
Name: "max-upstream-conns",
Usage: "Maximum concurrent connections to upstream. Setting to 0 means unlimited.",
Value: MaxUpstreamConnsDefault,
EnvVars: []string{"TUNNEL_DNS_MAX_UPSTREAM_CONNS"},
},
},
ArgsUsage: " ", // can't be the empty string or we get the default output
Hidden: hidden,
@@ -92,8 +99,10 @@ func Run(c *cli.Context) error {
uint16(c.Int("port")),
c.StringSlice("upstream"),
c.StringSlice("bootstrap"),
c.Int("max-upstream-conns"),
log,
)
if err != nil {
log.Err(err).Msg("Failed to create the listeners")
return err
@@ -175,12 +184,12 @@ func (l *Listener) Stop() error {
}
// CreateListener configures the server and bound sockets
func CreateListener(address string, port uint16, upstreams []string, bootstraps []string, log *zerolog.Logger) (*Listener, error) {
func CreateListener(address string, port uint16, upstreams []string, bootstraps []string, maxUpstreamConnections int, log *zerolog.Logger) (*Listener, error) {
// Build the list of upstreams
upstreamList := make([]Upstream, 0)
for _, url := range upstreams {
log.Info().Str(LogFieldURL, url).Msg("Adding DNS upstream")
upstream, err := NewUpstreamHTTPS(url, bootstraps, log)
upstream, err := NewUpstreamHTTPS(url, bootstraps, maxUpstreamConnections, log)
if err != nil {
return nil, errors.Wrap(err, "failed to create HTTPS upstream")
}
-3
View File
@@ -1,3 +0,0 @@
/examples/blog/blog
/examples/orders/orders
/examples/basic/basic
-25
View File
@@ -1,25 +0,0 @@
language: go
go_import_path: github.com/DATA-DOG/go-sqlmock
go:
- 1.2.x
- 1.3.x
- 1.4 # has no cover tool for latest releases
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
sudo: false
script:
- go vet
- test -z "$(go fmt ./...)" # fail if not formatted properly
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
-28
View File
@@ -1,28 +0,0 @@
The three clause BSD license (http://en.wikipedia.org/wiki/BSD_licenses)
Copyright (c) 2013-2019, DATA-DOG team
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name DataDog.lt may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-259
View File
@@ -1,259 +0,0 @@
[![Build Status](https://travis-ci.org/DATA-DOG/go-sqlmock.svg)](https://travis-ci.org/DATA-DOG/go-sqlmock)
[![GoDoc](https://godoc.org/github.com/DATA-DOG/go-sqlmock?status.svg)](https://godoc.org/github.com/DATA-DOG/go-sqlmock)
[![Go Report Card](https://goreportcard.com/badge/github.com/DATA-DOG/go-sqlmock)](https://goreportcard.com/report/github.com/DATA-DOG/go-sqlmock)
[![codecov.io](https://codecov.io/github/DATA-DOG/go-sqlmock/branch/master/graph/badge.svg)](https://codecov.io/github/DATA-DOG/go-sqlmock)
# Sql driver mock for Golang
**sqlmock** is a mock library implementing [sql/driver](https://godoc.org/database/sql/driver). Which has one and only
purpose - to simulate any **sql** driver behavior in tests, without needing a real database connection. It helps to
maintain correct **TDD** workflow.
- this library is now complete and stable. (you may not find new changes for this reason)
- supports concurrency and multiple connections.
- supports **go1.8** Context related feature mocking and Named sql parameters.
- does not require any modifications to your source code.
- the driver allows to mock any sql driver method behavior.
- has strict by default expectation order matching.
- has no third party dependencies.
**NOTE:** in **v1.2.0** **sqlmock.Rows** has changed to struct from interface, if you were using any type references to that
interface, you will need to switch it to a pointer struct type. Also, **sqlmock.Rows** were used to implement **driver.Rows**
interface, which was not required or useful for mocking and was removed. Hope it will not cause issues.
## Install
go get github.com/DATA-DOG/go-sqlmock
## Documentation and Examples
Visit [godoc](http://godoc.org/github.com/DATA-DOG/go-sqlmock) for general examples and public api reference.
See **.travis.yml** for supported **go** versions.
Different use case, is to functionally test with a real database - [go-txdb](https://github.com/DATA-DOG/go-txdb)
all database related actions are isolated within a single transaction so the database can remain in the same state.
See implementation examples:
- [blog API server](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/blog)
- [the same orders example](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/orders)
### Something you may want to test, assuming you use the [go-mysql-driver](https://github.com/go-sql-driver/mysql)
``` go
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func recordStats(db *sql.DB, userID, productID int64) (err error) {
tx, err := db.Begin()
if err != nil {
return
}
defer func() {
switch err {
case nil:
err = tx.Commit()
default:
tx.Rollback()
}
}()
if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
return
}
if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
return
}
return
}
func main() {
// @NOTE: the real connection is not required for tests
db, err := sql.Open("mysql", "root@/blog")
if err != nil {
panic(err)
}
defer db.Close()
if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
panic(err)
}
}
```
### Tests with sqlmock
``` go
package main
import (
"fmt"
"testing"
"github.com/DATA-DOG/go-sqlmock"
)
// a successful case
func TestShouldUpdateStats(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
mock.ExpectBegin()
mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
// now we execute our method
if err = recordStats(db, 2, 3); err != nil {
t.Errorf("error was not expected while updating stats: %s", err)
}
// we make sure that all expectations were met
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
// a failing test case
func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
mock.ExpectBegin()
mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec("INSERT INTO product_viewers").
WithArgs(2, 3).
WillReturnError(fmt.Errorf("some error"))
mock.ExpectRollback()
// now we execute our method
if err = recordStats(db, 2, 3); err == nil {
t.Errorf("was expecting an error, but there was none")
}
// we make sure that all expectations were met
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
```
## Customize SQL query matching
There were plenty of requests from users regarding SQL query string validation or different matching option.
We have now implemented the `QueryMatcher` interface, which can be passed through an option when calling
`sqlmock.New` or `sqlmock.NewWithDSN`.
This now allows to include some library, which would allow for example to parse and validate `mysql` SQL AST.
And create a custom QueryMatcher in order to validate SQL in sophisticated ways.
By default, **sqlmock** is preserving backward compatibility and default query matcher is `sqlmock.QueryMatcherRegexp`
which uses expected SQL string as a regular expression to match incoming query string. There is an equality matcher:
`QueryMatcherEqual` which will do a full case sensitive match.
In order to customize the QueryMatcher, use the following:
``` go
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
```
The query matcher can be fully customized based on user needs. **sqlmock** will not
provide a standard sql parsing matchers, since various drivers may not follow the same SQL standard.
## Matching arguments like time.Time
There may be arguments which are of `struct` type and cannot be compared easily by value like `time.Time`. In this case
**sqlmock** provides an [Argument](https://godoc.org/github.com/DATA-DOG/go-sqlmock#Argument) interface which
can be used in more sophisticated matching. Here is a simple example of time argument matching:
``` go
type AnyTime struct{}
// Match satisfies sqlmock.Argument interface
func (a AnyTime) Match(v driver.Value) bool {
_, ok := v.(time.Time)
return ok
}
func TestAnyTimeArgument(t *testing.T) {
t.Parallel()
db, mock, err := New()
if err != nil {
t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
mock.ExpectExec("INSERT INTO users").
WithArgs("john", AnyTime{}).
WillReturnResult(NewResult(1, 1))
_, err = db.Exec("INSERT INTO users(name, created_at) VALUES (?, ?)", "john", time.Now())
if err != nil {
t.Errorf("error '%s' was not expected, while inserting a row", err)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
```
It only asserts that argument is of `time.Time` type.
## Run tests
go test -race
## Change Log
- **2019-02-13** - added `go.mod` removed the references and suggestions using `gopkg.in`.
- **2018-12-11** - added expectation of Rows to be closed, while mocking expected query.
- **2018-12-11** - introduced an option to provide **QueryMatcher** in order to customize SQL query matching.
- **2017-09-01** - it is now possible to expect that prepared statement will be closed,
using **ExpectedPrepare.WillBeClosed**.
- **2017-02-09** - implemented support for **go1.8** features. **Rows** interface was changed to struct
but contains all methods as before and should maintain backwards compatibility. **ExpectedQuery.WillReturnRows** may now
accept multiple row sets.
- **2016-11-02** - `db.Prepare()` was not validating expected prepare SQL
query. It should still be validated even if Exec or Query is not
executed on that prepared statement.
- **2016-02-23** - added **sqlmock.AnyArg()** function to provide any kind
of argument matcher.
- **2016-02-23** - convert expected arguments to driver.Value as natural
driver does, the change may affect time.Time comparison and will be
stricter. See [issue](https://github.com/DATA-DOG/go-sqlmock/issues/31).
- **2015-08-27** - **v1** api change, concurrency support, all known issues fixed.
- **2014-08-16** instead of **panic** during reflect type mismatch when comparing query arguments - now return error
- **2014-08-14** added **sqlmock.NewErrorResult** which gives an option to return driver.Result with errors for
interface methods, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/5)
- **2014-05-29** allow to match arguments in more sophisticated ways, by providing an **sqlmock.Argument** interface
- **2014-04-21** introduce **sqlmock.New()** to open a mock database connection for tests. This method
calls sql.DB.Ping to ensure that connection is open, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/4).
This way on Close it will surely assert if all expectations are met, even if database was not triggered at all.
The old way is still available, but it is advisable to call db.Ping manually before asserting with db.Close.
- **2014-02-14** RowsFromCSVString is now a part of Rows interface named as FromCSVString.
It has changed to allow more ways to construct rows and to easily extend this API in future.
See [issue 1](https://github.com/DATA-DOG/go-sqlmock/issues/1)
**RowsFromCSVString** is deprecated and will be removed in future
## Contributions
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
please open an issue before, to discuss whether these changes can be accepted. All backward incompatible changes are
and will be treated cautiously
## License
The [three clause BSD license](http://en.wikipedia.org/wiki/BSD_licenses)
-24
View File
@@ -1,24 +0,0 @@
package sqlmock
import "database/sql/driver"
// Argument interface allows to match
// any argument in specific way when used with
// ExpectedQuery and ExpectedExec expectations.
type Argument interface {
Match(driver.Value) bool
}
// AnyArg will return an Argument which can
// match any kind of arguments.
//
// Useful for time.Time or similar kinds of arguments.
func AnyArg() Argument {
return anyArgument{}
}
type anyArgument struct{}
func (a anyArgument) Match(_ driver.Value) bool {
return true
}
-81
View File
@@ -1,81 +0,0 @@
package sqlmock
import (
"database/sql"
"database/sql/driver"
"fmt"
"sync"
)
var pool *mockDriver
func init() {
pool = &mockDriver{
conns: make(map[string]*sqlmock),
}
sql.Register("sqlmock", pool)
}
type mockDriver struct {
sync.Mutex
counter int
conns map[string]*sqlmock
}
func (d *mockDriver) Open(dsn string) (driver.Conn, error) {
d.Lock()
defer d.Unlock()
c, ok := d.conns[dsn]
if !ok {
return c, fmt.Errorf("expected a connection to be available, but it is not")
}
c.opened++
return c, nil
}
// New creates sqlmock database connection and a mock to manage expectations.
// Accepts options, like ValueConverterOption, to use a ValueConverter from
// a specific driver.
// Pings db so that all expectations could be
// asserted.
func New(options ...func(*sqlmock) error) (*sql.DB, Sqlmock, error) {
pool.Lock()
dsn := fmt.Sprintf("sqlmock_db_%d", pool.counter)
pool.counter++
smock := &sqlmock{dsn: dsn, drv: pool, ordered: true}
pool.conns[dsn] = smock
pool.Unlock()
return smock.open(options)
}
// NewWithDSN creates sqlmock database connection with a specific DSN
// and a mock to manage expectations.
// Accepts options, like ValueConverterOption, to use a ValueConverter from
// a specific driver.
// Pings db so that all expectations could be asserted.
//
// This method is introduced because of sql abstraction
// libraries, which do not provide a way to initialize
// with sql.DB instance. For example GORM library.
//
// Note, it will error if attempted to create with an
// already used dsn
//
// It is not recommended to use this method, unless you
// really need it and there is no other way around.
func NewWithDSN(dsn string, options ...func(*sqlmock) error) (*sql.DB, Sqlmock, error) {
pool.Lock()
if _, ok := pool.conns[dsn]; ok {
pool.Unlock()
return nil, nil, fmt.Errorf("cannot create a new mock database with the same dsn: %s", dsn)
}
smock := &sqlmock{dsn: dsn, drv: pool, ordered: true}
pool.conns[dsn] = smock
pool.Unlock()
return smock.open(options)
}
-355
View File
@@ -1,355 +0,0 @@
package sqlmock
import (
"database/sql/driver"
"fmt"
"strings"
"sync"
"time"
)
// an expectation interface
type expectation interface {
fulfilled() bool
Lock()
Unlock()
String() string
}
// common expectation struct
// satisfies the expectation interface
type commonExpectation struct {
sync.Mutex
triggered bool
err error
}
func (e *commonExpectation) fulfilled() bool {
return e.triggered
}
// ExpectedClose is used to manage *sql.DB.Close expectation
// returned by *Sqlmock.ExpectClose.
type ExpectedClose struct {
commonExpectation
}
// WillReturnError allows to set an error for *sql.DB.Close action
func (e *ExpectedClose) WillReturnError(err error) *ExpectedClose {
e.err = err
return e
}
// String returns string representation
func (e *ExpectedClose) String() string {
msg := "ExpectedClose => expecting database Close"
if e.err != nil {
msg += fmt.Sprintf(", which should return error: %s", e.err)
}
return msg
}
// ExpectedBegin is used to manage *sql.DB.Begin expectation
// returned by *Sqlmock.ExpectBegin.
type ExpectedBegin struct {
commonExpectation
delay time.Duration
}
// WillReturnError allows to set an error for *sql.DB.Begin action
func (e *ExpectedBegin) WillReturnError(err error) *ExpectedBegin {
e.err = err
return e
}
// String returns string representation
func (e *ExpectedBegin) String() string {
msg := "ExpectedBegin => expecting database transaction Begin"
if e.err != nil {
msg += fmt.Sprintf(", which should return error: %s", e.err)
}
return msg
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedBegin) WillDelayFor(duration time.Duration) *ExpectedBegin {
e.delay = duration
return e
}
// ExpectedCommit is used to manage *sql.Tx.Commit expectation
// returned by *Sqlmock.ExpectCommit.
type ExpectedCommit struct {
commonExpectation
}
// WillReturnError allows to set an error for *sql.Tx.Close action
func (e *ExpectedCommit) WillReturnError(err error) *ExpectedCommit {
e.err = err
return e
}
// String returns string representation
func (e *ExpectedCommit) String() string {
msg := "ExpectedCommit => expecting transaction Commit"
if e.err != nil {
msg += fmt.Sprintf(", which should return error: %s", e.err)
}
return msg
}
// ExpectedRollback is used to manage *sql.Tx.Rollback expectation
// returned by *Sqlmock.ExpectRollback.
type ExpectedRollback struct {
commonExpectation
}
// WillReturnError allows to set an error for *sql.Tx.Rollback action
func (e *ExpectedRollback) WillReturnError(err error) *ExpectedRollback {
e.err = err
return e
}
// String returns string representation
func (e *ExpectedRollback) String() string {
msg := "ExpectedRollback => expecting transaction Rollback"
if e.err != nil {
msg += fmt.Sprintf(", which should return error: %s", e.err)
}
return msg
}
// ExpectedQuery is used to manage *sql.DB.Query, *dql.DB.QueryRow, *sql.Tx.Query,
// *sql.Tx.QueryRow, *sql.Stmt.Query or *sql.Stmt.QueryRow expectations.
// Returned by *Sqlmock.ExpectQuery.
type ExpectedQuery struct {
queryBasedExpectation
rows driver.Rows
delay time.Duration
rowsMustBeClosed bool
rowsWereClosed bool
}
// WithArgs will match given expected args to actual database query arguments.
// if at least one argument does not match, it will return an error. For specific
// arguments an sqlmock.Argument interface can be used to match an argument.
func (e *ExpectedQuery) WithArgs(args ...driver.Value) *ExpectedQuery {
e.args = args
return e
}
// RowsWillBeClosed expects this query rows to be closed.
func (e *ExpectedQuery) RowsWillBeClosed() *ExpectedQuery {
e.rowsMustBeClosed = true
return e
}
// WillReturnError allows to set an error for expected database query
func (e *ExpectedQuery) WillReturnError(err error) *ExpectedQuery {
e.err = err
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedQuery) WillDelayFor(duration time.Duration) *ExpectedQuery {
e.delay = duration
return e
}
// String returns string representation
func (e *ExpectedQuery) String() string {
msg := "ExpectedQuery => expecting Query, QueryContext or QueryRow which:"
msg += "\n - matches sql: '" + e.expectSQL + "'"
if len(e.args) == 0 {
msg += "\n - is without arguments"
} else {
msg += "\n - is with arguments:\n"
for i, arg := range e.args {
msg += fmt.Sprintf(" %d - %+v\n", i, arg)
}
msg = strings.TrimSpace(msg)
}
if e.rows != nil {
msg += fmt.Sprintf("\n - %s", e.rows)
}
if e.err != nil {
msg += fmt.Sprintf("\n - should return error: %s", e.err)
}
return msg
}
// ExpectedExec is used to manage *sql.DB.Exec, *sql.Tx.Exec or *sql.Stmt.Exec expectations.
// Returned by *Sqlmock.ExpectExec.
type ExpectedExec struct {
queryBasedExpectation
result driver.Result
delay time.Duration
}
// WithArgs will match given expected args to actual database exec operation arguments.
// if at least one argument does not match, it will return an error. For specific
// arguments an sqlmock.Argument interface can be used to match an argument.
func (e *ExpectedExec) WithArgs(args ...driver.Value) *ExpectedExec {
e.args = args
return e
}
// WillReturnError allows to set an error for expected database exec action
func (e *ExpectedExec) WillReturnError(err error) *ExpectedExec {
e.err = err
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedExec) WillDelayFor(duration time.Duration) *ExpectedExec {
e.delay = duration
return e
}
// String returns string representation
func (e *ExpectedExec) String() string {
msg := "ExpectedExec => expecting Exec or ExecContext which:"
msg += "\n - matches sql: '" + e.expectSQL + "'"
if len(e.args) == 0 {
msg += "\n - is without arguments"
} else {
msg += "\n - is with arguments:\n"
var margs []string
for i, arg := range e.args {
margs = append(margs, fmt.Sprintf(" %d - %+v", i, arg))
}
msg += strings.Join(margs, "\n")
}
if e.result != nil {
res, _ := e.result.(*result)
msg += "\n - should return Result having:"
msg += fmt.Sprintf("\n LastInsertId: %d", res.insertID)
msg += fmt.Sprintf("\n RowsAffected: %d", res.rowsAffected)
if res.err != nil {
msg += fmt.Sprintf("\n Error: %s", res.err)
}
}
if e.err != nil {
msg += fmt.Sprintf("\n - should return error: %s", e.err)
}
return msg
}
// WillReturnResult arranges for an expected Exec() to return a particular
// result, there is sqlmock.NewResult(lastInsertID int64, affectedRows int64) method
// to build a corresponding result. Or if actions needs to be tested against errors
// sqlmock.NewErrorResult(err error) to return a given error.
func (e *ExpectedExec) WillReturnResult(result driver.Result) *ExpectedExec {
e.result = result
return e
}
// ExpectedPrepare is used to manage *sql.DB.Prepare or *sql.Tx.Prepare expectations.
// Returned by *Sqlmock.ExpectPrepare.
type ExpectedPrepare struct {
commonExpectation
mock *sqlmock
expectSQL string
statement driver.Stmt
closeErr error
mustBeClosed bool
wasClosed bool
delay time.Duration
}
// WillReturnError allows to set an error for the expected *sql.DB.Prepare or *sql.Tx.Prepare action.
func (e *ExpectedPrepare) WillReturnError(err error) *ExpectedPrepare {
e.err = err
return e
}
// WillReturnCloseError allows to set an error for this prepared statement Close action
func (e *ExpectedPrepare) WillReturnCloseError(err error) *ExpectedPrepare {
e.closeErr = err
return e
}
// WillDelayFor allows to specify duration for which it will delay
// result. May be used together with Context
func (e *ExpectedPrepare) WillDelayFor(duration time.Duration) *ExpectedPrepare {
e.delay = duration
return e
}
// WillBeClosed expects this prepared statement to
// be closed.
func (e *ExpectedPrepare) WillBeClosed() *ExpectedPrepare {
e.mustBeClosed = true
return e
}
// ExpectQuery allows to expect Query() or QueryRow() on this prepared statement.
// This method is convenient in order to prevent duplicating sql query string matching.
func (e *ExpectedPrepare) ExpectQuery() *ExpectedQuery {
eq := &ExpectedQuery{}
eq.expectSQL = e.expectSQL
eq.converter = e.mock.converter
e.mock.expected = append(e.mock.expected, eq)
return eq
}
// ExpectExec allows to expect Exec() on this prepared statement.
// This method is convenient in order to prevent duplicating sql query string matching.
func (e *ExpectedPrepare) ExpectExec() *ExpectedExec {
eq := &ExpectedExec{}
eq.expectSQL = e.expectSQL
eq.converter = e.mock.converter
e.mock.expected = append(e.mock.expected, eq)
return eq
}
// String returns string representation
func (e *ExpectedPrepare) String() string {
msg := "ExpectedPrepare => expecting Prepare statement which:"
msg += "\n - matches sql: '" + e.expectSQL + "'"
if e.err != nil {
msg += fmt.Sprintf("\n - should return error: %s", e.err)
}
if e.closeErr != nil {
msg += fmt.Sprintf("\n - should return error on Close: %s", e.closeErr)
}
return msg
}
// query based expectation
// adds a query matching logic
type queryBasedExpectation struct {
commonExpectation
expectSQL string
converter driver.ValueConverter
args []driver.Value
}
func (e *queryBasedExpectation) attemptArgMatch(args []namedValue) (err error) {
// catch panic
defer func() {
if e := recover(); e != nil {
_, ok := e.(error)
if !ok {
err = fmt.Errorf(e.(string))
}
}
}()
err = e.argsMatches(args)
return
}
-52
View File
@@ -1,52 +0,0 @@
// +build !go1.8
package sqlmock
import (
"database/sql/driver"
"fmt"
"reflect"
)
// WillReturnRows specifies the set of resulting rows that will be returned
// by the triggered query
func (e *ExpectedQuery) WillReturnRows(rows *Rows) *ExpectedQuery {
e.rows = &rowSets{sets: []*Rows{rows}, ex: e}
return e
}
func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
if nil == e.args {
return nil
}
if len(args) != len(e.args) {
return fmt.Errorf("expected %d, but got %d arguments", len(e.args), len(args))
}
for k, v := range args {
// custom argument matcher
matcher, ok := e.args[k].(Argument)
if ok {
// @TODO: does it make sense to pass value instead of named value?
if !matcher.Match(v.Value) {
return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
}
continue
}
dval := e.args[k]
// convert to driver converter
darg, err := e.converter.ConvertValue(dval)
if err != nil {
return fmt.Errorf("could not convert %d argument %T - %+v to driver value: %s", k, e.args[k], e.args[k], err)
}
if !driver.IsValue(darg) {
return fmt.Errorf("argument %d: non-subset type %T returned from Value", k, darg)
}
if !reflect.DeepEqual(darg, v.Value) {
return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, v.Value, v.Value)
}
}
return nil
}
-66
View File
@@ -1,66 +0,0 @@
// +build go1.8
package sqlmock
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
)
// WillReturnRows specifies the set of resulting rows that will be returned
// by the triggered query
func (e *ExpectedQuery) WillReturnRows(rows ...*Rows) *ExpectedQuery {
sets := make([]*Rows, len(rows))
for i, r := range rows {
sets[i] = r
}
e.rows = &rowSets{sets: sets, ex: e}
return e
}
func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
if nil == e.args {
return nil
}
if len(args) != len(e.args) {
return fmt.Errorf("expected %d, but got %d arguments", len(e.args), len(args))
}
// @TODO should we assert either all args are named or ordinal?
for k, v := range args {
// custom argument matcher
matcher, ok := e.args[k].(Argument)
if ok {
if !matcher.Match(v.Value) {
return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
}
continue
}
dval := e.args[k]
if named, isNamed := dval.(sql.NamedArg); isNamed {
dval = named.Value
if v.Name != named.Name {
return fmt.Errorf("named argument %d: name: \"%s\" does not match expected: \"%s\"", k, v.Name, named.Name)
}
} else if k+1 != v.Ordinal {
return fmt.Errorf("argument %d: ordinal position: %d does not match expected: %d", k, k+1, v.Ordinal)
}
// convert to driver converter
darg, err := e.converter.ConvertValue(dval)
if err != nil {
return fmt.Errorf("could not convert %d argument %T - %+v to driver value: %s", k, e.args[k], e.args[k], err)
}
if !driver.IsValue(darg) {
return fmt.Errorf("argument %d: non-subset type %T returned from Value", k, darg)
}
if !reflect.DeepEqual(darg, v.Value) {
return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, v.Value, v.Value)
}
}
return nil
}
-1
View File
@@ -1 +0,0 @@
module github.com/DATA-DOG/go-sqlmock
-22
View File
@@ -1,22 +0,0 @@
package sqlmock
import "database/sql/driver"
// ValueConverterOption allows to create a sqlmock connection
// with a custom ValueConverter to support drivers with special data types.
func ValueConverterOption(converter driver.ValueConverter) func(*sqlmock) error {
return func(s *sqlmock) error {
s.converter = converter
return nil
}
}
// QueryMatcherOption allows to customize SQL query matcher
// and match SQL query strings in more sophisticated ways.
// The default QueryMatcher is QueryMatcherRegexp.
func QueryMatcherOption(queryMatcher QueryMatcher) func(*sqlmock) error {
return func(s *sqlmock) error {
s.queryMatcher = queryMatcher
return nil
}
}
-68
View File
@@ -1,68 +0,0 @@
package sqlmock
import (
"fmt"
"regexp"
"strings"
)
var re = regexp.MustCompile("\\s+")
// strip out new lines and trim spaces
func stripQuery(q string) (s string) {
return strings.TrimSpace(re.ReplaceAllString(q, " "))
}
// QueryMatcher is an SQL query string matcher interface,
// which can be used to customize validation of SQL query strings.
// As an example, external library could be used to build
// and validate SQL ast, columns selected.
//
// sqlmock can be customized to implement a different QueryMatcher
// configured through an option when sqlmock.New or sqlmock.NewWithDSN
// is called, default QueryMatcher is QueryMatcherRegexp.
type QueryMatcher interface {
// Match expected SQL query string without whitespace to
// actual SQL.
Match(expectedSQL, actualSQL string) error
}
// QueryMatcherFunc type is an adapter to allow the use of
// ordinary functions as QueryMatcher. If f is a function
// with the appropriate signature, QueryMatcherFunc(f) is a
// QueryMatcher that calls f.
type QueryMatcherFunc func(expectedSQL, actualSQL string) error
// Match implements the QueryMatcher
func (f QueryMatcherFunc) Match(expectedSQL, actualSQL string) error {
return f(expectedSQL, actualSQL)
}
// QueryMatcherRegexp is the default SQL query matcher
// used by sqlmock. It parses expectedSQL to a regular
// expression and attempts to match actualSQL.
var QueryMatcherRegexp QueryMatcher = QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
expect := stripQuery(expectedSQL)
actual := stripQuery(actualSQL)
re, err := regexp.Compile(expect)
if err != nil {
return err
}
if !re.MatchString(actual) {
return fmt.Errorf(`could not match actual sql: "%s" with expected regexp "%s"`, actual, re.String())
}
return nil
})
// QueryMatcherEqual is the SQL query matcher
// which simply tries a case sensitive match of
// expected and actual SQL strings without whitespace.
var QueryMatcherEqual QueryMatcher = QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
expect := stripQuery(expectedSQL)
actual := stripQuery(actualSQL)
if actual != expect {
return fmt.Errorf(`actual sql: "%s" does not equal to expected "%s"`, actual, expect)
}
return nil
})
-39
View File
@@ -1,39 +0,0 @@
package sqlmock
import (
"database/sql/driver"
)
// Result satisfies sql driver Result, which
// holds last insert id and rows affected
// by Exec queries
type result struct {
insertID int64
rowsAffected int64
err error
}
// NewResult creates a new sql driver Result
// for Exec based query mocks.
func NewResult(lastInsertID int64, rowsAffected int64) driver.Result {
return &result{
insertID: lastInsertID,
rowsAffected: rowsAffected,
}
}
// NewErrorResult creates a new sql driver Result
// which returns an error given for both interface methods
func NewErrorResult(err error) driver.Result {
return &result{
err: err,
}
}
func (r *result) LastInsertId() (int64, error) {
return r.insertID, r.err
}
func (r *result) RowsAffected() (int64, error) {
return r.rowsAffected, r.err
}
-176
View File
@@ -1,176 +0,0 @@
package sqlmock
import (
"database/sql/driver"
"encoding/csv"
"fmt"
"io"
"strings"
)
// CSVColumnParser is a function which converts trimmed csv
// column string to a []byte representation. Currently
// transforms NULL to nil
var CSVColumnParser = func(s string) []byte {
switch {
case strings.ToLower(s) == "null":
return nil
}
return []byte(s)
}
type rowSets struct {
sets []*Rows
pos int
ex *ExpectedQuery
}
func (rs *rowSets) Columns() []string {
return rs.sets[rs.pos].cols
}
func (rs *rowSets) Close() error {
rs.ex.rowsWereClosed = true
return rs.sets[rs.pos].closeErr
}
// advances to next row
func (rs *rowSets) Next(dest []driver.Value) error {
r := rs.sets[rs.pos]
r.pos++
if r.pos > len(r.rows) {
return io.EOF // per interface spec
}
for i, col := range r.rows[r.pos-1] {
dest[i] = col
}
return r.nextErr[r.pos-1]
}
// transforms to debuggable printable string
func (rs *rowSets) String() string {
if rs.empty() {
return "with empty rows"
}
msg := "should return rows:\n"
if len(rs.sets) == 1 {
for n, row := range rs.sets[0].rows {
msg += fmt.Sprintf(" row %d - %+v\n", n, row)
}
return strings.TrimSpace(msg)
}
for i, set := range rs.sets {
msg += fmt.Sprintf(" result set: %d\n", i)
for n, row := range set.rows {
msg += fmt.Sprintf(" row %d - %+v\n", n, row)
}
}
return strings.TrimSpace(msg)
}
func (rs *rowSets) empty() bool {
for _, set := range rs.sets {
if len(set.rows) > 0 {
return false
}
}
return true
}
// Rows is a mocked collection of rows to
// return for Query result
type Rows struct {
converter driver.ValueConverter
cols []string
rows [][]driver.Value
pos int
nextErr map[int]error
closeErr error
}
// NewRows allows Rows to be created from a
// sql driver.Value slice or from the CSV string and
// to be used as sql driver.Rows.
// Use Sqlmock.NewRows instead if using a custom converter
func NewRows(columns []string) *Rows {
return &Rows{
cols: columns,
nextErr: make(map[int]error),
converter: driver.DefaultParameterConverter,
}
}
// CloseError allows to set an error
// which will be returned by rows.Close
// function.
//
// The close error will be triggered only in cases
// when rows.Next() EOF was not yet reached, that is
// a default sql library behavior
func (r *Rows) CloseError(err error) *Rows {
r.closeErr = err
return r
}
// RowError allows to set an error
// which will be returned when a given
// row number is read
func (r *Rows) RowError(row int, err error) *Rows {
r.nextErr[row] = err
return r
}
// AddRow composed from database driver.Value slice
// return the same instance to perform subsequent actions.
// Note that the number of values must match the number
// of columns
func (r *Rows) AddRow(values ...driver.Value) *Rows {
if len(values) != len(r.cols) {
panic("Expected number of values to match number of columns")
}
row := make([]driver.Value, len(r.cols))
for i, v := range values {
// Convert user-friendly values (such as int or driver.Valuer)
// to database/sql native value (driver.Value such as int64)
var err error
v, err = r.converter.ConvertValue(v)
if err != nil {
panic(fmt.Errorf(
"row #%d, column #%d (%q) type %T: %s",
len(r.rows)+1, i, r.cols[i], values[i], err,
))
}
row[i] = v
}
r.rows = append(r.rows, row)
return r
}
// FromCSVString build rows from csv string.
// return the same instance to perform subsequent actions.
// Note that the number of values must match the number
// of columns
func (r *Rows) FromCSVString(s string) *Rows {
res := strings.NewReader(strings.TrimSpace(s))
csvReader := csv.NewReader(res)
for {
res, err := csvReader.Read()
if err != nil || res == nil {
break
}
row := make([]driver.Value, len(r.cols))
for i, v := range res {
row[i] = CSVColumnParser(strings.TrimSpace(v))
}
r.rows = append(r.rows, row)
}
return r
}
-20
View File
@@ -1,20 +0,0 @@
// +build go1.8
package sqlmock
import "io"
// Implement the "RowsNextResultSet" interface
func (rs *rowSets) HasNextResultSet() bool {
return rs.pos+1 < len(rs.sets)
}
// Implement the "RowsNextResultSet" interface
func (rs *rowSets) NextResultSet() error {
if !rs.HasNextResultSet() {
return io.EOF
}
rs.pos++
return nil
}
-589
View File
@@ -1,589 +0,0 @@
/*
Package sqlmock is a mock library implementing sql driver. Which has one and only
purpose - to simulate any sql driver behavior in tests, without needing a real
database connection. It helps to maintain correct **TDD** workflow.
It does not require any modifications to your source code in order to test
and mock database operations. Supports concurrency and multiple database mocking.
The driver allows to mock any sql driver method behavior.
*/
package sqlmock
import (
"database/sql"
"database/sql/driver"
"fmt"
"time"
)
// Sqlmock interface serves to create expectations
// for any kind of database action in order to mock
// and test real database behavior.
type Sqlmock interface {
// ExpectClose queues an expectation for this database
// action to be triggered. the *ExpectedClose allows
// to mock database response
ExpectClose() *ExpectedClose
// ExpectationsWereMet checks whether all queued expectations
// were met in order. If any of them was not met - an error is returned.
ExpectationsWereMet() error
// ExpectPrepare expects Prepare() to be called with expectedSQL query.
// the *ExpectedPrepare allows to mock database response.
// Note that you may expect Query() or Exec() on the *ExpectedPrepare
// statement to prevent repeating expectedSQL
ExpectPrepare(expectedSQL string) *ExpectedPrepare
// ExpectQuery expects Query() or QueryRow() to be called with expectedSQL query.
// the *ExpectedQuery allows to mock database response.
ExpectQuery(expectedSQL string) *ExpectedQuery
// ExpectExec expects Exec() to be called with expectedSQL query.
// the *ExpectedExec allows to mock database response
ExpectExec(expectedSQL string) *ExpectedExec
// ExpectBegin expects *sql.DB.Begin to be called.
// the *ExpectedBegin allows to mock database response
ExpectBegin() *ExpectedBegin
// ExpectCommit expects *sql.Tx.Commit to be called.
// the *ExpectedCommit allows to mock database response
ExpectCommit() *ExpectedCommit
// ExpectRollback expects *sql.Tx.Rollback to be called.
// the *ExpectedRollback allows to mock database response
ExpectRollback() *ExpectedRollback
// MatchExpectationsInOrder gives an option whether to match all
// expectations in the order they were set or not.
//
// By default it is set to - true. But if you use goroutines
// to parallelize your query executation, that option may
// be handy.
//
// This option may be turned on anytime during tests. As soon
// as it is switched to false, expectations will be matched
// in any order. Or otherwise if switched to true, any unmatched
// expectations will be expected in order
MatchExpectationsInOrder(bool)
// NewRows allows Rows to be created from a
// sql driver.Value slice or from the CSV string and
// to be used as sql driver.Rows.
NewRows(columns []string) *Rows
}
type sqlmock struct {
ordered bool
dsn string
opened int
drv *mockDriver
converter driver.ValueConverter
queryMatcher QueryMatcher
expected []expectation
}
func (c *sqlmock) open(options []func(*sqlmock) error) (*sql.DB, Sqlmock, error) {
db, err := sql.Open("sqlmock", c.dsn)
if err != nil {
return db, c, err
}
for _, option := range options {
err := option(c)
if err != nil {
return db, c, err
}
}
if c.converter == nil {
c.converter = driver.DefaultParameterConverter
}
if c.queryMatcher == nil {
c.queryMatcher = QueryMatcherRegexp
}
return db, c, db.Ping()
}
func (c *sqlmock) ExpectClose() *ExpectedClose {
e := &ExpectedClose{}
c.expected = append(c.expected, e)
return e
}
func (c *sqlmock) MatchExpectationsInOrder(b bool) {
c.ordered = b
}
// Close a mock database driver connection. It may or may not
// be called depending on the circumstances, but if it is called
// there must be an *ExpectedClose expectation satisfied.
// meets http://golang.org/pkg/database/sql/driver/#Conn interface
func (c *sqlmock) Close() error {
c.drv.Lock()
defer c.drv.Unlock()
c.opened--
if c.opened == 0 {
delete(c.drv.conns, c.dsn)
}
var expected *ExpectedClose
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if expected, ok = next.(*ExpectedClose); ok {
break
}
next.Unlock()
if c.ordered {
return fmt.Errorf("call to database Close, was not expected, next expectation is: %s", next)
}
}
if expected == nil {
msg := "call to database Close was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt.Errorf(msg)
}
expected.triggered = true
expected.Unlock()
return expected.err
}
func (c *sqlmock) ExpectationsWereMet() error {
for _, e := range c.expected {
e.Lock()
fulfilled := e.fulfilled()
e.Unlock()
if !fulfilled {
return fmt.Errorf("there is a remaining expectation which was not matched: %s", e)
}
// for expected prepared statement check whether it was closed if expected
if prep, ok := e.(*ExpectedPrepare); ok {
if prep.mustBeClosed && !prep.wasClosed {
return fmt.Errorf("expected prepared statement to be closed, but it was not: %s", prep)
}
}
// must check whether all expected queried rows are closed
if query, ok := e.(*ExpectedQuery); ok {
if query.rowsMustBeClosed && !query.rowsWereClosed {
return fmt.Errorf("expected query rows to be closed, but it was not: %s", query)
}
}
}
return nil
}
// Begin meets http://golang.org/pkg/database/sql/driver/#Conn interface
func (c *sqlmock) Begin() (driver.Tx, error) {
ex, err := c.begin()
if ex != nil {
time.Sleep(ex.delay)
}
if err != nil {
return nil, err
}
return c, nil
}
func (c *sqlmock) begin() (*ExpectedBegin, error) {
var expected *ExpectedBegin
var ok bool
var fulfilled int
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if expected, ok = next.(*ExpectedBegin); ok {
break
}
next.Unlock()
if c.ordered {
return nil, fmt.Errorf("call to database transaction Begin, was not expected, next expectation is: %s", next)
}
}
if expected == nil {
msg := "call to database transaction Begin was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg)
}
expected.triggered = true
expected.Unlock()
return expected, expected.err
}
func (c *sqlmock) ExpectBegin() *ExpectedBegin {
e := &ExpectedBegin{}
c.expected = append(c.expected, e)
return e
}
// Exec meets http://golang.org/pkg/database/sql/driver/#Execer
func (c *sqlmock) Exec(query string, args []driver.Value) (driver.Result, error) {
namedArgs := make([]namedValue, len(args))
for i, v := range args {
namedArgs[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
}
ex, err := c.exec(query, namedArgs)
if ex != nil {
time.Sleep(ex.delay)
}
if err != nil {
return nil, err
}
return ex.result, nil
}
func (c *sqlmock) exec(query string, args []namedValue) (*ExpectedExec, error) {
var expected *ExpectedExec
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if c.ordered {
if expected, ok = next.(*ExpectedExec); ok {
break
}
next.Unlock()
return nil, fmt.Errorf("call to ExecQuery '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
}
if exec, ok := next.(*ExpectedExec); ok {
if err := c.queryMatcher.Match(exec.expectSQL, query); err != nil {
next.Unlock()
continue
}
if err := exec.attemptArgMatch(args); err == nil {
expected = exec
break
}
}
next.Unlock()
}
if expected == nil {
msg := "call to ExecQuery '%s' with args %+v was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
}
defer expected.Unlock()
if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
return nil, fmt.Errorf("ExecQuery: %v", err)
}
if err := expected.argsMatches(args); err != nil {
return nil, fmt.Errorf("ExecQuery '%s', arguments do not match: %s", query, err)
}
expected.triggered = true
if expected.err != nil {
return expected, expected.err // mocked to return error
}
if expected.result == nil {
return nil, fmt.Errorf("ExecQuery '%s' with args %+v, must return a database/sql/driver.Result, but it was not set for expectation %T as %+v", query, args, expected, expected)
}
return expected, nil
}
func (c *sqlmock) ExpectExec(expectedSQL string) *ExpectedExec {
e := &ExpectedExec{}
e.expectSQL = expectedSQL
e.converter = c.converter
c.expected = append(c.expected, e)
return e
}
// Prepare meets http://golang.org/pkg/database/sql/driver/#Conn interface
func (c *sqlmock) Prepare(query string) (driver.Stmt, error) {
ex, err := c.prepare(query)
if ex != nil {
time.Sleep(ex.delay)
}
if err != nil {
return nil, err
}
return &statement{c, ex, query}, nil
}
func (c *sqlmock) prepare(query string) (*ExpectedPrepare, error) {
var expected *ExpectedPrepare
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if c.ordered {
if expected, ok = next.(*ExpectedPrepare); ok {
break
}
next.Unlock()
return nil, fmt.Errorf("call to Prepare statement with query '%s', was not expected, next expectation is: %s", query, next)
}
if pr, ok := next.(*ExpectedPrepare); ok {
if err := c.queryMatcher.Match(pr.expectSQL, query); err == nil {
expected = pr
break
}
}
next.Unlock()
}
if expected == nil {
msg := "call to Prepare '%s' query was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query)
}
defer expected.Unlock()
if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
return nil, fmt.Errorf("Prepare: %v", err)
}
expected.triggered = true
return expected, expected.err
}
func (c *sqlmock) ExpectPrepare(expectedSQL string) *ExpectedPrepare {
e := &ExpectedPrepare{expectSQL: expectedSQL, mock: c}
c.expected = append(c.expected, e)
return e
}
type namedValue struct {
Name string
Ordinal int
Value driver.Value
}
// Query meets http://golang.org/pkg/database/sql/driver/#Queryer
func (c *sqlmock) Query(query string, args []driver.Value) (driver.Rows, error) {
namedArgs := make([]namedValue, len(args))
for i, v := range args {
namedArgs[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
}
ex, err := c.query(query, namedArgs)
if ex != nil {
time.Sleep(ex.delay)
}
if err != nil {
return nil, err
}
return ex.rows, nil
}
func (c *sqlmock) query(query string, args []namedValue) (*ExpectedQuery, error) {
var expected *ExpectedQuery
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if c.ordered {
if expected, ok = next.(*ExpectedQuery); ok {
break
}
next.Unlock()
return nil, fmt.Errorf("call to Query '%s' with args %+v, was not expected, next expectation is: %s", query, args, next)
}
if qr, ok := next.(*ExpectedQuery); ok {
if err := c.queryMatcher.Match(qr.expectSQL, query); err != nil {
next.Unlock()
continue
}
if err := qr.attemptArgMatch(args); err == nil {
expected = qr
break
}
}
next.Unlock()
}
if expected == nil {
msg := "call to Query '%s' with args %+v was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return nil, fmt.Errorf(msg, query, args)
}
defer expected.Unlock()
if err := c.queryMatcher.Match(expected.expectSQL, query); err != nil {
return nil, fmt.Errorf("Query: %v", err)
}
if err := expected.argsMatches(args); err != nil {
return nil, fmt.Errorf("Query '%s', arguments do not match: %s", query, err)
}
expected.triggered = true
if expected.err != nil {
return expected, expected.err // mocked to return error
}
if expected.rows == nil {
return nil, fmt.Errorf("Query '%s' with args %+v, must return a database/sql/driver.Rows, but it was not set for expectation %T as %+v", query, args, expected, expected)
}
return expected, nil
}
func (c *sqlmock) ExpectQuery(expectedSQL string) *ExpectedQuery {
e := &ExpectedQuery{}
e.expectSQL = expectedSQL
e.converter = c.converter
c.expected = append(c.expected, e)
return e
}
func (c *sqlmock) ExpectCommit() *ExpectedCommit {
e := &ExpectedCommit{}
c.expected = append(c.expected, e)
return e
}
func (c *sqlmock) ExpectRollback() *ExpectedRollback {
e := &ExpectedRollback{}
c.expected = append(c.expected, e)
return e
}
// Commit meets http://golang.org/pkg/database/sql/driver/#Tx
func (c *sqlmock) Commit() error {
var expected *ExpectedCommit
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if expected, ok = next.(*ExpectedCommit); ok {
break
}
next.Unlock()
if c.ordered {
return fmt.Errorf("call to Commit transaction, was not expected, next expectation is: %s", next)
}
}
if expected == nil {
msg := "call to Commit transaction was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt.Errorf(msg)
}
expected.triggered = true
expected.Unlock()
return expected.err
}
// Rollback meets http://golang.org/pkg/database/sql/driver/#Tx
func (c *sqlmock) Rollback() error {
var expected *ExpectedRollback
var fulfilled int
var ok bool
for _, next := range c.expected {
next.Lock()
if next.fulfilled() {
next.Unlock()
fulfilled++
continue
}
if expected, ok = next.(*ExpectedRollback); ok {
break
}
next.Unlock()
if c.ordered {
return fmt.Errorf("call to Rollback transaction, was not expected, next expectation is: %s", next)
}
}
if expected == nil {
msg := "call to Rollback transaction was not expected"
if fulfilled == len(c.expected) {
msg = "all expectations were already fulfilled, " + msg
}
return fmt.Errorf(msg)
}
expected.triggered = true
expected.Unlock()
return expected.err
}
// NewRows allows Rows to be created from a
// sql driver.Value slice or from the CSV string and
// to be used as sql driver.Rows.
func (c *sqlmock) NewRows(columns []string) *Rows {
r := NewRows(columns)
r.converter = c.converter
return r
}
-121
View File
@@ -1,121 +0,0 @@
// +build go1.8
package sqlmock
import (
"context"
"database/sql/driver"
"errors"
"time"
)
// ErrCancelled defines an error value, which can be expected in case of
// such cancellation error.
var ErrCancelled = errors.New("canceling query due to user request")
// Implement the "QueryerContext" interface
func (c *sqlmock) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
namedArgs := make([]namedValue, len(args))
for i, nv := range args {
namedArgs[i] = namedValue(nv)
}
ex, err := c.query(query, namedArgs)
if ex != nil {
select {
case <-time.After(ex.delay):
if err != nil {
return nil, err
}
return ex.rows, nil
case <-ctx.Done():
return nil, ErrCancelled
}
}
return nil, err
}
// Implement the "ExecerContext" interface
func (c *sqlmock) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
namedArgs := make([]namedValue, len(args))
for i, nv := range args {
namedArgs[i] = namedValue(nv)
}
ex, err := c.exec(query, namedArgs)
if ex != nil {
select {
case <-time.After(ex.delay):
if err != nil {
return nil, err
}
return ex.result, nil
case <-ctx.Done():
return nil, ErrCancelled
}
}
return nil, err
}
// Implement the "ConnBeginTx" interface
func (c *sqlmock) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
ex, err := c.begin()
if ex != nil {
select {
case <-time.After(ex.delay):
if err != nil {
return nil, err
}
return c, nil
case <-ctx.Done():
return nil, ErrCancelled
}
}
return nil, err
}
// Implement the "ConnPrepareContext" interface
func (c *sqlmock) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
ex, err := c.prepare(query)
if ex != nil {
select {
case <-time.After(ex.delay):
if err != nil {
return nil, err
}
return &statement{c, ex, query}, nil
case <-ctx.Done():
return nil, ErrCancelled
}
}
return nil, err
}
// Implement the "Pinger" interface
// for now we do not have a Ping expectation
// may be something for the future
func (c *sqlmock) Ping(ctx context.Context) error {
return nil
}
// Implement the "StmtExecContext" interface
func (stmt *statement) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
return stmt.conn.ExecContext(ctx, stmt.query, args)
}
// Implement the "StmtQueryContext" interface
func (stmt *statement) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
return stmt.conn.QueryContext(ctx, stmt.query, args)
}
// @TODO maybe add ExpectedBegin.WithOptions(driver.TxOptions)
// CheckNamedValue meets https://golang.org/pkg/database/sql/driver/#NamedValueChecker
func (c *sqlmock) CheckNamedValue(nv *driver.NamedValue) (err error) {
nv.Value, err = c.converter.ConvertValue(nv.Value)
return err
}
-28
View File
@@ -1,28 +0,0 @@
package sqlmock
import (
"database/sql/driver"
)
type statement struct {
conn *sqlmock
ex *ExpectedPrepare
query string
}
func (stmt *statement) Close() error {
stmt.ex.wasClosed = true
return stmt.ex.closeErr
}
func (stmt *statement) NumInput() int {
return -1
}
func (stmt *statement) Exec(args []driver.Value) (driver.Result, error) {
return stmt.conn.Exec(stmt.query, args)
}
func (stmt *statement) Query(args []driver.Value) (driver.Rows, error) {
return stmt.conn.Query(stmt.query, args)
}
-22
View File
@@ -1,22 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
-27
View File
@@ -1,27 +0,0 @@
Copyright (c) 2013 CloudFlare, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the CloudFlare, Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-14
View File
@@ -1,14 +0,0 @@
GCFLAGS :=
LDFLAGS :=
.PHONY: install
install:
@go install -v .
.PHONY: test
test:
@go test -gcflags='$(GCFLAGS)' -ldflags='$(LDFLAGS)' .
.PHONY: bench
bench:
@go test -gcflags='$(GCFLAGS)' -ldflags='$(LDFLAGS)' -bench .
-4
View File
@@ -1,4 +0,0 @@
golz4
=====
Golang interface to LZ4 compression
-4
View File
@@ -1,4 +0,0 @@
// Package lz4 implements compression using lz4.c and lz4hc.c
//
// Copyright (c) 2013 CloudFlare, Inc.
package lz4
-55
View File
@@ -1,55 +0,0 @@
package lz4
// #cgo CFLAGS: -O3
// #include "src/lz4.h"
// #include "src/lz4.c"
import "C"
import (
"errors"
"fmt"
"unsafe"
)
// p gets a char pointer to the first byte of a []byte slice
func p(in []byte) *C.char {
if len(in) == 0 {
return (*C.char)(unsafe.Pointer(nil))
}
return (*C.char)(unsafe.Pointer(&in[0]))
}
// clen gets the length of a []byte slice as a char *
func clen(s []byte) C.int {
return C.int(len(s))
}
// Uncompress with a known output size. len(out) should be equal to
// the length of the uncompressed out.
func Uncompress(in, out []byte) (error) {
if int(C.LZ4_decompress_safe(p(in), p(out), clen(in), clen(out))) < 0 {
return errors.New("Malformed compression stream")
}
return nil
}
// CompressBound calculates the size of the output buffer needed by
// Compress. This is based on the following macro:
//
// #define LZ4_COMPRESSBOUND(isize)
// ((unsigned int)(isize) > (unsigned int)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
func CompressBound(in []byte) int {
return len(in) + ((len(in) / 255) + 16)
}
// Compress compresses in and puts the content in out. len(out)
// should have enough space for the compressed data (use CompressBound
// to calculate). Returns the number of bytes in the out slice.
func Compress(in, out []byte) (outSize int, err error) {
outSize = int(C.LZ4_compress_limitedOutput(p(in), p(out), clen(in), clen(out)))
if outSize == 0 {
err = fmt.Errorf("insufficient space for compression")
}
return
}
-38
View File
@@ -1,38 +0,0 @@
package lz4
// #cgo CFLAGS: -O3
// #include "src/lz4hc.h"
// #include "src/lz4hc.c"
import "C"
import (
"fmt"
)
// CompressHC compresses in and puts the content in out. len(out)
// should have enough space for the compressed data (use CompressBound
// to calculate). Returns the number of bytes in the out slice. Determines
// the compression level automatically.
func CompressHC(in, out []byte) (int, error) {
// 0 automatically sets the compression level.
return CompressHCLevel(in, out, 0)
}
// CompressHCLevel compresses in at the given compression level and puts the
// content in out. len(out) should have enough space for the compressed data
// (use CompressBound to calculate). Returns the number of bytes in the out
// slice. To automatically choose the compression level, use 0. Otherwise, use
// any value in the inclusive range 1 (worst) through 16 (best). Most
// applications will prefer CompressHC.
func CompressHCLevel(in, out []byte, level int) (outSize int, err error) {
// LZ4HC does not handle empty buffers. Pass through to Compress.
if len(in) == 0 || len(out) == 0 {
return Compress(in, out)
}
outSize = int(C.LZ4_compressHC2_limitedOutput(p(in), p(out), clen(in), clen(out), C.int(level)))
if outSize == 0 {
err = fmt.Errorf("insufficient space for compression")
}
return
}
-143
View File
@@ -1,143 +0,0 @@
CANTO I
IN the midway of this our mortal life,
I found me in a gloomy wood, astray
Gone from the path direct: and e'en to tell
It were no easy task, how savage wild
That forest, how robust and rough its growth,
Which to remember only, my dismay
Renews, in bitterness not far from death.
Yet to discourse of what there good befell,
All else will I relate discover'd there.
How first I enter'd it I scarce can say,
Such sleepy dullness in that instant weigh'd
My senses down, when the true path I left,
But when a mountain's foot I reach'd, where clos'd
The valley, that had pierc'd my heart with dread,
I look'd aloft, and saw his shoulders broad
Already vested with that planet's beam,
Who leads all wanderers safe through every way.
Then was a little respite to the fear,
That in my heart's recesses deep had lain,
All of that night, so pitifully pass'd:
And as a man, with difficult short breath,
Forespent with toiling, 'scap'd from sea to shore,
Turns to the perilous wide waste, and stands
At gaze; e'en so my spirit, that yet fail'd
Struggling with terror, turn'd to view the straits,
That none hath pass'd and liv'd. My weary frame
After short pause recomforted, again
I journey'd on over that lonely steep,
The hinder foot still firmer. Scarce the ascent
Began, when, lo! a panther, nimble, light,
And cover'd with a speckled skin, appear'd,
Nor, when it saw me, vanish'd, rather strove
To check my onward going; that ofttimes
With purpose to retrace my steps I turn'd.
The hour was morning's prime, and on his way
Aloft the sun ascended with those stars,
That with him rose, when Love divine first mov'd
Those its fair works: so that with joyous hope
All things conspir'd to fill me, the gay skin
Of that swift animal, the matin dawn
And the sweet season. Soon that joy was chas'd,
And by new dread succeeded, when in view
A lion came, 'gainst me, as it appear'd,
With his head held aloft and hunger-mad,
That e'en the air was fear-struck. A she-wolf
Was at his heels, who in her leanness seem'd
Full of all wants, and many a land hath made
Disconsolate ere now. She with such fear
O'erwhelmed me, at the sight of her appall'd,
That of the height all hope I lost. As one,
Who with his gain elated, sees the time
When all unwares is gone, he inwardly
Mourns with heart-griping anguish; such was I,
Haunted by that fell beast, never at peace,
Who coming o'er against me, by degrees
Impell'd me where the sun in silence rests.
While to the lower space with backward step
I fell, my ken discern'd the form one of one,
Whose voice seem'd faint through long disuse of speech.
When him in that great desert I espied,
"Have mercy on me!" cried I out aloud,
"Spirit! or living man! what e'er thou be!"
He answer'd: "Now not man, man once I was,
And born of Lombard parents, Mantuana both
By country, when the power of Julius yet
Was scarcely firm. At Rome my life was past
Beneath the mild Augustus, in the time
Of fabled deities and false. A bard
Was I, and made Anchises' upright son
The subject of my song, who came from Troy,
When the flames prey'd on Ilium's haughty towers.
But thou, say wherefore to such perils past
Return'st thou? wherefore not this pleasant mount
Ascendest, cause and source of all delight?"
"And art thou then that Virgil, that well-spring,
From which such copious floods of eloquence
Have issued?" I with front abash'd replied.
"Glory and light of all the tuneful train!
May it avail me that I long with zeal
Have sought thy volume, and with love immense
Have conn'd it o'er. My master thou and guide!
Thou he from whom alone I have deriv'd
That style, which for its beauty into fame
Exalts me. See the beast, from whom I fled.
O save me from her, thou illustrious sage!"
"For every vein and pulse throughout my frame
She hath made tremble." He, soon as he saw
That I was weeping, answer'd, "Thou must needs
Another way pursue, if thou wouldst 'scape
From out that savage wilderness. This beast,
At whom thou criest, her way will suffer none
To pass, and no less hindrance makes than death:
So bad and so accursed in her kind,
That never sated is her ravenous will,
Still after food more craving than before.
To many an animal in wedlock vile
She fastens, and shall yet to many more,
Until that greyhound come, who shall destroy
Her with sharp pain. He will not life support
By earth nor its base metals, but by love,
Wisdom, and virtue, and his land shall be
The land 'twixt either Feltro. In his might
Shall safety to Italia's plains arise,
For whose fair realm, Camilla, virgin pure,
Nisus, Euryalus, and Turnus fell.
He with incessant chase through every town
Shall worry, until he to hell at length
Restore her, thence by envy first let loose.
I for thy profit pond'ring now devise,
That thou mayst follow me, and I thy guide
Will lead thee hence through an eternal space,
Where thou shalt hear despairing shrieks, and see
Spirits of old tormented, who invoke
A second death; and those next view, who dwell
Content in fire, for that they hope to come,
Whene'er the time may be, among the blest,
Into whose regions if thou then desire
T' ascend, a spirit worthier then I
Must lead thee, in whose charge, when I depart,
Thou shalt be left: for that Almighty King,
Who reigns above, a rebel to his law,
Adjudges me, and therefore hath decreed,
That to his city none through me should come.
He in all parts hath sway; there rules, there holds
His citadel and throne. O happy those,
Whom there he chooses!" I to him in few:
"Bard! by that God, whom thou didst not adore,
I do beseech thee (that this ill and worse
I may escape) to lead me, where thou saidst,
That I Saint Peter's gate may view, and those
Who as thou tell'st, are in such dismal plight."
Onward he mov'd, I close his steps pursu'd.
-27
View File
@@ -1,27 +0,0 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-289
View File
@@ -1,289 +0,0 @@
# A pure Go MSSQL driver for Go's database/sql package
[![GoDoc](https://godoc.org/github.com/denisenkom/go-mssqldb?status.svg)](http://godoc.org/github.com/denisenkom/go-mssqldb)
[![Build status](https://ci.appveyor.com/api/projects/status/jrln8cs62wj9i0a2?svg=true)](https://ci.appveyor.com/project/denisenkom/go-mssqldb)
[![codecov](https://codecov.io/gh/denisenkom/go-mssqldb/branch/master/graph/badge.svg)](https://codecov.io/gh/denisenkom/go-mssqldb)
## Install
Requires Go 1.8 or above.
Install with `go get github.com/denisenkom/go-mssqldb` .
## Connection Parameters and DSN
The recommended connection string uses a URL format:
`sqlserver://username:password@host/instance?param1=value&param2=value`
Other supported formats are listed below.
### Common parameters:
* `user id` - enter the SQL Server Authentication user id or the Windows Authentication user id in the DOMAIN\User format. On Windows, if user id is empty or missing Single-Sign-On is used.
* `password`
* `database`
* `connection timeout` - in seconds (default is 0 for no timeout), set to 0 for no timeout. Recommended to set to 0 and use context to manage query and connection timeouts.
* `dial timeout` - in seconds (default is 15), set to 0 for no timeout
* `encrypt`
* `disable` - Data send between client and server is not encrypted.
* `false` - Data sent between client and server is not encrypted beyond the login packet. (Default)
* `true` - Data sent between client and server is encrypted.
* `app name` - The application name (default is go-mssqldb)
### Connection parameters for ODBC and ADO style connection strings:
* `server` - host or host\instance (default localhost)
* `port` - used only when there is no instance in server (default 1433)
### Less common parameters:
* `keepAlive` - in seconds; 0 to disable (default is 30)
* `failoverpartner` - host or host\instance (default is no partner).
* `failoverport` - used only when there is no instance in failoverpartner (default 1433)
* `packet size` - in bytes; 512 to 32767 (default is 4096)
* Encrypted connections have a maximum packet size of 16383 bytes
* Further information on usage: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option
* `log` - logging flags (default 0/no logging, 63 for full logging)
* 1 log errors
* 2 log messages
* 4 log rows affected
* 8 trace sql statements
* 16 log statement parameters
* 32 log transaction begin/end
* `TrustServerCertificate`
* false - Server certificate is checked. Default is false if encypt is specified.
* true - Server certificate is not checked. Default is true if encrypt is not specified. If trust server certificate is true, driver accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.
* `certificate` - The file that contains the public key certificate of the CA that signed the SQL Server certificate. The specified certificate overrides the go platform specific CA certificates.
* `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host.
* `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port.
* `Workstation ID` - The workstation name (default is the host name)
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`.
### The connection string can be specified in one of three formats:
1. URL: with `sqlserver` scheme. username and password appears before the host. Any instance appears as
the first segment in the path. All other options are query parameters. Examples:
* `sqlserver://username:password@host/instance?param1=value&param2=value`
* `sqlserver://username:password@host:port?param1=value&param2=value`
* `sqlserver://sa@localhost/SQLExpress?database=master&connection+timeout=30` // `SQLExpress instance.
* `sqlserver://sa:mypass@localhost?database=master&connection+timeout=30` // username=sa, password=mypass.
* `sqlserver://sa:mypass@localhost:1234?database=master&connection+timeout=30` // port 1234 on localhost.
* `sqlserver://sa:my%7Bpass@somehost?connection+timeout=30` // password is "my{pass"
A string of this format can be constructed using the `URL` type in the `net/url` package.
```go
query := url.Values{}
query.Add("app name", "MyAppName")
u := &url.URL{
Scheme: "sqlserver",
User: url.UserPassword(username, password),
Host: fmt.Sprintf("%s:%d", hostname, port),
// Path: instance, // if connecting to an instance instead of a port
RawQuery: query.Encode(),
}
db, err := sql.Open("sqlserver", u.String())
```
2. ADO: `key=value` pairs separated by `;`. Values may not contain `;`, leading and trailing whitespace is ignored.
Examples:
* `server=localhost\\SQLExpress;user id=sa;database=master;app name=MyAppName`
* `server=localhost;user id=sa;database=master;app name=MyAppName`
3. ODBC: Prefix with `odbc`, `key=value` pairs separated by `;`. Allow `;` by wrapping
values in `{}`. Examples:
* `odbc:server=localhost\\SQLExpress;user id=sa;database=master;app name=MyAppName`
* `odbc:server=localhost;user id=sa;database=master;app name=MyAppName`
* `odbc:server=localhost;user id=sa;password={foo;bar}` // Value marked with `{}`, password is "foo;bar"
* `odbc:server=localhost;user id=sa;password={foo{bar}` // Value marked with `{}`, password is "foo{bar"
* `odbc:server=localhost;user id=sa;password={foobar }` // Value marked with `{}`, password is "foobar "
* `odbc:server=localhost;user id=sa;password=foo{bar` // Literal `{`, password is "foo{bar"
* `odbc:server=localhost;user id=sa;password=foo}bar` // Literal `}`, password is "foo}bar"
* `odbc:server=localhost;user id=sa;password={foo{bar}` // Literal `{`, password is "foo{bar"
* `odbc:server=localhost;user id=sa;password={foo}}bar}` // Escaped `} with `}}`, password is "foo}bar"
## Executing Stored Procedures
To run a stored procedure, set the query text to the procedure name:
```go
var account = "abc"
_, err := db.ExecContext(ctx, "sp_RunMe",
sql.Named("ID", 123),
sql.Named("Account", sql.Out{Dest: &account}),
)
```
## Reading Output Parameters from a Stored Procedure with Resultset
To read output parameters from a stored procedure with resultset, make sure you read all the rows before reading the output parameters:
```go
sqltextcreate := `
CREATE PROCEDURE spwithoutputandrows
@bitparam BIT OUTPUT
AS BEGIN
SET @bitparam = 1
SELECT 'Row 1'
END
`
var bitout int64
rows, err := db.QueryContext(ctx, "spwithoutputandrows", sql.Named("bitparam", sql.Out{Dest: &bitout}))
var strrow string
for rows.Next() {
err = rows.Scan(&strrow)
}
fmt.Printf("bitparam is %d", bitout)
```
## Caveat for local temporary tables
Due to protocol limitations, temporary tables will only be allocated on the connection
as a result of executing a query with zero parameters. The following query
will, due to the use of a parameter, execute in its own session,
and `#mytemp` will be de-allocated right away:
```go
conn, err := pool.Conn(ctx)
defer conn.Close()
_, err := conn.ExecContext(ctx, "select @p1 as x into #mytemp", 1)
// at this point #mytemp is already dropped again as the session of the ExecContext is over
```
To work around this, always explicitly create the local temporary
table in a query without any parameters. As a special case, the driver
will then be able to execute the query directly on the
connection-scoped session. The following example works:
```go
conn, err := pool.Conn(ctx)
// Set us up so that temp table is always cleaned up, since conn.Close()
// merely returns conn to pool, rather than actually closing the connection.
defer func() {
_, _ = conn.ExecContext(ctx, "drop table #mytemp") // always clean up
conn.Close() // merely returns conn to pool
}()
// Since we not pass any parameters below, the query will execute on the scope of
// the connection and succeed in creating the table.
_, err := conn.ExecContext(ctx, "create table #mytemp ( x int )")
// #mytemp is now available even if you pass parameters
_, err := conn.ExecContext(ctx, "insert into #mytemp (x) values (@p1)", 1)
```
## Return Status
To get the procedure return status, pass into the parameters a
`*mssql.ReturnStatus`. For example:
```
var rs mssql.ReturnStatus
_, err := db.ExecContext(ctx, "theproc", &rs)
log.Printf("status=%d", rs)
```
or
```
var rs mssql.ReturnStatus
_, err := db.QueryContext(ctx, "theproc", &rs)
for rows.Next() {
err = rows.Scan(&val)
}
log.Printf("status=%d", rs)
```
Limitation: ReturnStatus cannot be retrieved using `QueryRow`.
## Parameters
The `sqlserver` driver uses normal MS SQL Server syntax and expects parameters in
the sql query to be in the form of either `@Name` or `@p1` to `@pN` (ordinal position).
```go
db.QueryContext(ctx, `select * from t where ID = @ID and Name = @p2;`, sql.Named("ID", 6), "Bob")
```
### Parameter Types
To pass specific types to the query parameters, say `varchar` or `date` types,
you must convert the types to the type before passing in. The following types
are supported:
* string -> nvarchar
* mssql.VarChar -> varchar
* time.Time -> datetimeoffset or datetime (TDS version dependent)
* mssql.DateTime1 -> datetime
* mssql.DateTimeOffset -> datetimeoffset
* "github.com/golang-sql/civil".Date -> date
* "github.com/golang-sql/civil".DateTime -> datetime2
* "github.com/golang-sql/civil".Time -> time
* mssql.TVP -> Table Value Parameter (TDS version dependent)
## Important Notes
* [LastInsertId](https://golang.org/pkg/database/sql/#Result.LastInsertId) should
not be used with this driver (or SQL Server) due to how the TDS protocol
works. Please use the [OUTPUT Clause](https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
or add a `select ID = convert(bigint, SCOPE_IDENTITY());` to the end of your
query (ref [SCOPE_IDENTITY](https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql)).
This will ensure you are getting the correct ID and will prevent a network round trip.
* [NewConnector](https://godoc.org/github.com/denisenkom/go-mssqldb#NewConnector)
may be used with [OpenDB](https://golang.org/pkg/database/sql/#OpenDB).
* [Connector.SessionInitSQL](https://godoc.org/github.com/denisenkom/go-mssqldb#Connector.SessionInitSQL)
may be set to set any driver specific session settings after the session
has been reset. If empty the session will still be reset but use the database
defaults in Go1.10+.
## Features
* Can be used with SQL Server 2005 or newer
* Can be used with Microsoft Azure SQL Database
* Can be used on all go supported platforms (e.g. Linux, Mac OS X and Windows)
* Supports new date/time types: date, time, datetime2, datetimeoffset
* Supports string parameters longer than 8000 characters
* Supports encryption using SSL/TLS
* Supports SQL Server and Windows Authentication
* Supports Single-Sign-On on Windows
* Supports connections to AlwaysOn Availability Group listeners, including re-direction to read-only replicas.
* Supports query notifications
## Tests
`go test` is used for testing. A running instance of MSSQL server is required.
Environment variables are used to pass login information.
Example:
env SQLSERVER_DSN=sqlserver://user:pass@hostname/instance?database=test1 go test
## Deprecated
These features still exist in the driver, but they are are deprecated.
### Query Parameter Token Replace (driver "mssql")
If you use the driver name "mssql" (rather then "sqlserver") the SQL text
will be loosly parsed and an attempt to extract identifiers using one of
* ?
* ?nnn
* :nnn
* $nnn
will be used. This is not recommended with SQL Server.
There is at least one existing `won't fix` issue with the query parsing.
Use the native "@Name" parameters instead with the "sqlserver" driver name.
## Known Issues
* SQL Server 2008 and 2008 R2 engine cannot handle login records when SSL encryption is not disabled.
To fix SQL Server 2008 R2 issue, install SQL Server 2008 R2 Service Pack 2.
To fix SQL Server 2008 issue, install Microsoft SQL Server 2008 Service Pack 3 and Cumulative update package 3 for SQL Server 2008 SP3.
More information: http://support.microsoft.com/kb/2653857
-50
View File
@@ -1,50 +0,0 @@
version: 1.0.{build}
os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\denisenkom\go-mssqldb
environment:
GOPATH: c:\gopath
HOST: localhost
SQLUSER: sa
SQLPASSWORD: Password12!
DATABASE: test
GOVERSION: 111
matrix:
- GOVERSION: 18
SQLINSTANCE: SQL2016
- GOVERSION: 19
SQLINSTANCE: SQL2016
- GOVERSION: 110
SQLINSTANCE: SQL2016
- GOVERSION: 111
SQLINSTANCE: SQL2016
- SQLINSTANCE: SQL2014
- SQLINSTANCE: SQL2012SP1
- SQLINSTANCE: SQL2008R2SP2
install:
- set GOROOT=c:\go%GOVERSION%
- set PATH=%GOPATH%\bin;%GOROOT%\bin;%PATH%
- go version
- go env
- go get -u github.com/golang-sql/civil
build_script:
- go build
before_test:
# setup SQL Server
- ps: |
$instanceName = $env:SQLINSTANCE
Start-Service "MSSQL`$$instanceName"
Start-Service "SQLBrowser"
- sqlcmd -S "(local)\%SQLINSTANCE%" -Q "Use [master]; CREATE DATABASE test;"
- sqlcmd -S "(local)\%SQLINSTANCE%" -h -1 -Q "set nocount on; Select @@version"
- pip install codecov
test_script:
- go test -race -cpu 4 -coverprofile=coverage.txt -covermode=atomic
- codecov -f coverage.txt
-262
View File
@@ -1,262 +0,0 @@
package mssql
import (
"encoding/binary"
"errors"
"io"
)
type packetType uint8
type header struct {
PacketType packetType
Status uint8
Size uint16
Spid uint16
PacketNo uint8
Pad uint8
}
// tdsBuffer reads and writes TDS packets of data to the transport.
// The write and read buffers are separate to make sending attn signals
// possible without locks. Currently attn signals are only sent during
// reads, not writes.
type tdsBuffer struct {
transport io.ReadWriteCloser
packetSize int
// Write fields.
wbuf []byte
wpos int
wPacketSeq byte
wPacketType packetType
// Read fields.
rbuf []byte
rpos int
rsize int
final bool
rPacketType packetType
// afterFirst is assigned to right after tdsBuffer is created and
// before the first use. It is executed after the first packet is
// written and then removed.
afterFirst func()
}
func newTdsBuffer(bufsize uint16, transport io.ReadWriteCloser) *tdsBuffer {
return &tdsBuffer{
packetSize: int(bufsize),
wbuf: make([]byte, 1<<16),
rbuf: make([]byte, 1<<16),
rpos: 8,
transport: transport,
}
}
func (rw *tdsBuffer) ResizeBuffer(packetSize int) {
rw.packetSize = packetSize
}
func (w *tdsBuffer) PackageSize() int {
return w.packetSize
}
func (w *tdsBuffer) flush() (err error) {
// Write packet size.
w.wbuf[0] = byte(w.wPacketType)
binary.BigEndian.PutUint16(w.wbuf[2:], uint16(w.wpos))
w.wbuf[6] = w.wPacketSeq
// Write packet into underlying transport.
if _, err = w.transport.Write(w.wbuf[:w.wpos]); err != nil {
return err
}
// It is possible to create a whole new buffer after a flush.
// Useful for debugging. Normally reuse the buffer.
// w.wbuf = make([]byte, 1<<16)
// Execute afterFirst hook if it is set.
if w.afterFirst != nil {
w.afterFirst()
w.afterFirst = nil
}
w.wpos = 8
w.wPacketSeq++
return nil
}
func (w *tdsBuffer) Write(p []byte) (total int, err error) {
for {
copied := copy(w.wbuf[w.wpos:w.packetSize], p)
w.wpos += copied
total += copied
if copied == len(p) {
return
}
if err = w.flush(); err != nil {
return
}
p = p[copied:]
}
}
func (w *tdsBuffer) WriteByte(b byte) error {
if int(w.wpos) == len(w.wbuf) || w.wpos == w.packetSize {
if err := w.flush(); err != nil {
return err
}
}
w.wbuf[w.wpos] = b
w.wpos += 1
return nil
}
func (w *tdsBuffer) BeginPacket(packetType packetType, resetSession bool) {
status := byte(0)
if resetSession {
switch packetType {
// Reset session can only be set on the following packet types.
case packSQLBatch, packRPCRequest, packTransMgrReq:
status = 0x8
}
}
w.wbuf[1] = status // Packet is incomplete. This byte is set again in FinishPacket.
w.wpos = 8
w.wPacketSeq = 1
w.wPacketType = packetType
}
func (w *tdsBuffer) FinishPacket() error {
w.wbuf[1] |= 1 // Mark this as the last packet in the message.
return w.flush()
}
var headerSize = binary.Size(header{})
func (r *tdsBuffer) readNextPacket() error {
h := header{}
var err error
err = binary.Read(r.transport, binary.BigEndian, &h)
if err != nil {
return err
}
if int(h.Size) > r.packetSize {
return errors.New("Invalid packet size, it is longer than buffer size")
}
if headerSize > int(h.Size) {
return errors.New("Invalid packet size, it is shorter than header size")
}
_, err = io.ReadFull(r.transport, r.rbuf[headerSize:h.Size])
if err != nil {
return err
}
r.rpos = headerSize
r.rsize = int(h.Size)
r.final = h.Status != 0
r.rPacketType = h.PacketType
return nil
}
func (r *tdsBuffer) BeginRead() (packetType, error) {
err := r.readNextPacket()
if err != nil {
return 0, err
}
return r.rPacketType, nil
}
func (r *tdsBuffer) ReadByte() (res byte, err error) {
if r.rpos == r.rsize {
if r.final {
return 0, io.EOF
}
err = r.readNextPacket()
if err != nil {
return 0, err
}
}
res = r.rbuf[r.rpos]
r.rpos++
return res, nil
}
func (r *tdsBuffer) byte() byte {
b, err := r.ReadByte()
if err != nil {
badStreamPanic(err)
}
return b
}
func (r *tdsBuffer) ReadFull(buf []byte) {
_, err := io.ReadFull(r, buf[:])
if err != nil {
badStreamPanic(err)
}
}
func (r *tdsBuffer) uint64() uint64 {
var buf [8]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint64(buf[:])
}
func (r *tdsBuffer) int32() int32 {
return int32(r.uint32())
}
func (r *tdsBuffer) uint32() uint32 {
var buf [4]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint32(buf[:])
}
func (r *tdsBuffer) uint16() uint16 {
var buf [2]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint16(buf[:])
}
func (r *tdsBuffer) BVarChar() string {
return readBVarCharOrPanic(r)
}
func readBVarCharOrPanic(r io.Reader) string {
s, err := readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
return s
}
func readUsVarCharOrPanic(r io.Reader) string {
s, err := readUsVarChar(r)
if err != nil {
badStreamPanic(err)
}
return s
}
func (r *tdsBuffer) UsVarChar() string {
return readUsVarCharOrPanic(r)
}
func (r *tdsBuffer) Read(buf []byte) (copied int, err error) {
copied = 0
err = nil
if r.rpos == r.rsize {
if r.final {
return 0, io.EOF
}
err = r.readNextPacket()
if err != nil {
return
}
}
copied = copy(buf, r.rbuf[r.rpos:r.rsize])
r.rpos += copied
return
}
-583
View File
@@ -1,583 +0,0 @@
package mssql
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"math"
"reflect"
"strings"
"time"
"github.com/denisenkom/go-mssqldb/internal/decimal"
)
type Bulk struct {
// ctx is used only for AddRow and Done methods.
// This could be removed if AddRow and Done accepted
// a ctx field as well, which is available with the
// database/sql call.
ctx context.Context
cn *Conn
metadata []columnStruct
bulkColumns []columnStruct
columnsName []string
tablename string
numRows int
headerSent bool
Options BulkOptions
Debug bool
}
type BulkOptions struct {
CheckConstraints bool
FireTriggers bool
KeepNulls bool
KilobytesPerBatch int
RowsPerBatch int
Order []string
Tablock bool
}
type DataValue interface{}
const (
sqlDateFormat = "2006-01-02"
sqlTimeFormat = "2006-01-02 15:04:05.999999999Z07:00"
)
func (cn *Conn) CreateBulk(table string, columns []string) (_ *Bulk) {
b := Bulk{ctx: context.Background(), cn: cn, tablename: table, headerSent: false, columnsName: columns}
b.Debug = false
return &b
}
func (cn *Conn) CreateBulkContext(ctx context.Context, table string, columns []string) (_ *Bulk) {
b := Bulk{ctx: ctx, cn: cn, tablename: table, headerSent: false, columnsName: columns}
b.Debug = false
return &b
}
func (b *Bulk) sendBulkCommand(ctx context.Context) (err error) {
//get table columns info
err = b.getMetadata(ctx)
if err != nil {
return err
}
//match the columns
for _, colname := range b.columnsName {
var bulkCol *columnStruct
for _, m := range b.metadata {
if m.ColName == colname {
bulkCol = &m
break
}
}
if bulkCol != nil {
if bulkCol.ti.TypeId == typeUdt {
//send udt as binary
bulkCol.ti.TypeId = typeBigVarBin
}
b.bulkColumns = append(b.bulkColumns, *bulkCol)
b.dlogf("Adding column %s %s %#x", colname, bulkCol.ColName, bulkCol.ti.TypeId)
} else {
return fmt.Errorf("Column %s does not exist in destination table %s", colname, b.tablename)
}
}
//create the bulk command
//columns definitions
var col_defs bytes.Buffer
for i, col := range b.bulkColumns {
if i != 0 {
col_defs.WriteString(", ")
}
col_defs.WriteString("[" + col.ColName + "] " + makeDecl(col.ti))
}
//options
var with_opts []string
if b.Options.CheckConstraints {
with_opts = append(with_opts, "CHECK_CONSTRAINTS")
}
if b.Options.FireTriggers {
with_opts = append(with_opts, "FIRE_TRIGGERS")
}
if b.Options.KeepNulls {
with_opts = append(with_opts, "KEEP_NULLS")
}
if b.Options.KilobytesPerBatch > 0 {
with_opts = append(with_opts, fmt.Sprintf("KILOBYTES_PER_BATCH = %d", b.Options.KilobytesPerBatch))
}
if b.Options.RowsPerBatch > 0 {
with_opts = append(with_opts, fmt.Sprintf("ROWS_PER_BATCH = %d", b.Options.RowsPerBatch))
}
if len(b.Options.Order) > 0 {
with_opts = append(with_opts, fmt.Sprintf("ORDER(%s)", strings.Join(b.Options.Order, ",")))
}
if b.Options.Tablock {
with_opts = append(with_opts, "TABLOCK")
}
var with_part string
if len(with_opts) > 0 {
with_part = fmt.Sprintf("WITH (%s)", strings.Join(with_opts, ","))
}
query := fmt.Sprintf("INSERT BULK %s (%s) %s", b.tablename, col_defs.String(), with_part)
stmt, err := b.cn.PrepareContext(ctx, query)
if err != nil {
return fmt.Errorf("Prepare failed: %s", err.Error())
}
b.dlogf(query)
_, err = stmt.(*Stmt).ExecContext(ctx, nil)
if err != nil {
return err
}
b.headerSent = true
var buf = b.cn.sess.buf
buf.BeginPacket(packBulkLoadBCP, false)
// Send the columns metadata.
columnMetadata := b.createColMetadata()
_, err = buf.Write(columnMetadata)
return
}
// AddRow immediately writes the row to the destination table.
// The arguments are the row values in the order they were specified.
func (b *Bulk) AddRow(row []interface{}) (err error) {
if !b.headerSent {
err = b.sendBulkCommand(b.ctx)
if err != nil {
return
}
}
if len(row) != len(b.bulkColumns) {
return fmt.Errorf("Row does not have the same number of columns than the destination table %d %d",
len(row), len(b.bulkColumns))
}
bytes, err := b.makeRowData(row)
if err != nil {
return
}
_, err = b.cn.sess.buf.Write(bytes)
if err != nil {
return
}
b.numRows = b.numRows + 1
return
}
func (b *Bulk) makeRowData(row []interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(tokenRow))
var logcol bytes.Buffer
for i, col := range b.bulkColumns {
if b.Debug {
logcol.WriteString(fmt.Sprintf(" col[%d]='%v' ", i, row[i]))
}
param, err := b.makeParam(row[i], col)
if err != nil {
return nil, fmt.Errorf("bulkcopy: %s", err.Error())
}
if col.ti.Writer == nil {
return nil, fmt.Errorf("no writer for column: %s, TypeId: %#x",
col.ColName, col.ti.TypeId)
}
err = col.ti.Writer(buf, param.ti, param.buffer)
if err != nil {
return nil, fmt.Errorf("bulkcopy: %s", err.Error())
}
}
b.dlogf("row[%d] %s\n", b.numRows, logcol.String())
return buf.Bytes(), nil
}
func (b *Bulk) Done() (rowcount int64, err error) {
if b.headerSent == false {
//no rows had been sent
return 0, nil
}
var buf = b.cn.sess.buf
buf.WriteByte(byte(tokenDone))
binary.Write(buf, binary.LittleEndian, uint16(doneFinal))
binary.Write(buf, binary.LittleEndian, uint16(0)) // curcmd
if b.cn.sess.loginAck.TDSVersion >= verTDS72 {
binary.Write(buf, binary.LittleEndian, uint64(0)) //rowcount 0
} else {
binary.Write(buf, binary.LittleEndian, uint32(0)) //rowcount 0
}
buf.FinishPacket()
tokchan := make(chan tokenStruct, 5)
go processResponse(b.ctx, b.cn.sess, tokchan, nil)
var rowCount int64
for token := range tokchan {
switch token := token.(type) {
case doneStruct:
if token.Status&doneCount != 0 {
rowCount = int64(token.RowCount)
}
if token.isError() {
return 0, token.getError()
}
case error:
return 0, b.cn.checkBadConn(token)
}
}
return rowCount, nil
}
func (b *Bulk) createColMetadata() []byte {
buf := new(bytes.Buffer)
buf.WriteByte(byte(tokenColMetadata)) // token
binary.Write(buf, binary.LittleEndian, uint16(len(b.bulkColumns))) // column count
for i, col := range b.bulkColumns {
if b.cn.sess.loginAck.TDSVersion >= verTDS72 {
binary.Write(buf, binary.LittleEndian, uint32(col.UserType)) // usertype, always 0?
} else {
binary.Write(buf, binary.LittleEndian, uint16(col.UserType))
}
binary.Write(buf, binary.LittleEndian, uint16(col.Flags))
writeTypeInfo(buf, &b.bulkColumns[i].ti)
if col.ti.TypeId == typeNText ||
col.ti.TypeId == typeText ||
col.ti.TypeId == typeImage {
tablename_ucs2 := str2ucs2(b.tablename)
binary.Write(buf, binary.LittleEndian, uint16(len(tablename_ucs2)/2))
buf.Write(tablename_ucs2)
}
colname_ucs2 := str2ucs2(col.ColName)
buf.WriteByte(uint8(len(colname_ucs2) / 2))
buf.Write(colname_ucs2)
}
return buf.Bytes()
}
func (b *Bulk) getMetadata(ctx context.Context) (err error) {
stmt, err := b.cn.prepareContext(ctx, "SET FMTONLY ON")
if err != nil {
return
}
_, err = stmt.ExecContext(ctx, nil)
if err != nil {
return
}
// Get columns info.
stmt, err = b.cn.prepareContext(ctx, fmt.Sprintf("select * from %s SET FMTONLY OFF", b.tablename))
if err != nil {
return
}
rows, err := stmt.QueryContext(ctx, nil)
if err != nil {
return fmt.Errorf("get columns info failed: %v", err)
}
b.metadata = rows.(*Rows).cols
if b.Debug {
for _, col := range b.metadata {
b.dlogf("col: %s typeId: %#x size: %d scale: %d prec: %d flags: %d lcid: %#x\n",
col.ColName, col.ti.TypeId, col.ti.Size, col.ti.Scale, col.ti.Prec,
col.Flags, col.ti.Collation.LcidAndFlags)
}
}
return rows.Close()
}
func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) {
res.ti.Size = col.ti.Size
res.ti.TypeId = col.ti.TypeId
if val == nil {
res.ti.Size = 0
return
}
switch col.ti.TypeId {
case typeInt1, typeInt2, typeInt4, typeInt8, typeIntN:
var intvalue int64
switch val := val.(type) {
case int:
intvalue = int64(val)
case int32:
intvalue = int64(val)
case int64:
intvalue = val
default:
err = fmt.Errorf("mssql: invalid type for int column: %T", val)
return
}
res.buffer = make([]byte, res.ti.Size)
if col.ti.Size == 1 {
res.buffer[0] = byte(intvalue)
} else if col.ti.Size == 2 {
binary.LittleEndian.PutUint16(res.buffer, uint16(intvalue))
} else if col.ti.Size == 4 {
binary.LittleEndian.PutUint32(res.buffer, uint32(intvalue))
} else if col.ti.Size == 8 {
binary.LittleEndian.PutUint64(res.buffer, uint64(intvalue))
}
case typeFlt4, typeFlt8, typeFltN:
var floatvalue float64
switch val := val.(type) {
case float32:
floatvalue = float64(val)
case float64:
floatvalue = val
case int:
floatvalue = float64(val)
case int64:
floatvalue = float64(val)
default:
err = fmt.Errorf("mssql: invalid type for float column: %T %s", val, val)
return
}
if col.ti.Size == 4 {
res.buffer = make([]byte, 4)
binary.LittleEndian.PutUint32(res.buffer, math.Float32bits(float32(floatvalue)))
} else if col.ti.Size == 8 {
res.buffer = make([]byte, 8)
binary.LittleEndian.PutUint64(res.buffer, math.Float64bits(floatvalue))
}
case typeNVarChar, typeNText, typeNChar:
switch val := val.(type) {
case string:
res.buffer = str2ucs2(val)
case []byte:
res.buffer = val
default:
err = fmt.Errorf("mssql: invalid type for nvarchar column: %T %s", val, val)
return
}
res.ti.Size = len(res.buffer)
case typeVarChar, typeBigVarChar, typeText, typeChar, typeBigChar:
switch val := val.(type) {
case string:
res.buffer = []byte(val)
case []byte:
res.buffer = val
default:
err = fmt.Errorf("mssql: invalid type for varchar column: %T %s", val, val)
return
}
res.ti.Size = len(res.buffer)
case typeBit, typeBitN:
if reflect.TypeOf(val).Kind() != reflect.Bool {
err = fmt.Errorf("mssql: invalid type for bit column: %T %s", val, val)
return
}
res.ti.TypeId = typeBitN
res.ti.Size = 1
res.buffer = make([]byte, 1)
if val.(bool) {
res.buffer[0] = 1
}
case typeDateTime2N:
switch val := val.(type) {
case time.Time:
res.buffer = encodeDateTime2(val, int(col.ti.Scale))
res.ti.Size = len(res.buffer)
case string:
var t time.Time
if t, err = time.Parse(sqlTimeFormat, val); err != nil {
return res, fmt.Errorf("bulk: unable to convert string to date: %v", err)
}
res.buffer = encodeDateTime2(t, int(col.ti.Scale))
res.ti.Size = len(res.buffer)
default:
err = fmt.Errorf("mssql: invalid type for datetime2 column: %T %s", val, val)
return
}
case typeDateTimeOffsetN:
switch val := val.(type) {
case time.Time:
res.buffer = encodeDateTimeOffset(val, int(col.ti.Scale))
res.ti.Size = len(res.buffer)
case string:
var t time.Time
if t, err = time.Parse(sqlTimeFormat, val); err != nil {
return res, fmt.Errorf("bulk: unable to convert string to date: %v", err)
}
res.buffer = encodeDateTimeOffset(t, int(col.ti.Scale))
res.ti.Size = len(res.buffer)
default:
err = fmt.Errorf("mssql: invalid type for datetimeoffset column: %T %s", val, val)
return
}
case typeDateN:
switch val := val.(type) {
case time.Time:
res.buffer = encodeDate(val)
res.ti.Size = len(res.buffer)
case string:
var t time.Time
if t, err = time.ParseInLocation(sqlDateFormat, val, time.UTC); err != nil {
return res, fmt.Errorf("bulk: unable to convert string to date: %v", err)
}
res.buffer = encodeDate(t)
res.ti.Size = len(res.buffer)
default:
err = fmt.Errorf("mssql: invalid type for date column: %T %s", val, val)
return
}
case typeDateTime, typeDateTimeN, typeDateTim4:
var t time.Time
switch val := val.(type) {
case time.Time:
t = val
case string:
if t, err = time.Parse(sqlTimeFormat, val); err != nil {
return res, fmt.Errorf("bulk: unable to convert string to date: %v", err)
}
default:
err = fmt.Errorf("mssql: invalid type for datetime column: %T %s", val, val)
return
}
if col.ti.Size == 4 {
res.buffer = encodeDateTim4(t)
res.ti.Size = len(res.buffer)
} else if col.ti.Size == 8 {
res.buffer = encodeDateTime(t)
res.ti.Size = len(res.buffer)
} else {
err = fmt.Errorf("mssql: invalid size of column %d", col.ti.Size)
}
// case typeMoney, typeMoney4, typeMoneyN:
case typeDecimal, typeDecimalN, typeNumeric, typeNumericN:
prec := col.ti.Prec
scale := col.ti.Scale
var dec decimal.Decimal
switch v := val.(type) {
case int:
dec = decimal.Int64ToDecimalScale(int64(v), 0)
case int8:
dec = decimal.Int64ToDecimalScale(int64(v), 0)
case int16:
dec = decimal.Int64ToDecimalScale(int64(v), 0)
case int32:
dec = decimal.Int64ToDecimalScale(int64(v), 0)
case int64:
dec = decimal.Int64ToDecimalScale(int64(v), 0)
case float32:
dec, err = decimal.Float64ToDecimalScale(float64(v), scale)
case float64:
dec, err = decimal.Float64ToDecimalScale(float64(v), scale)
case string:
dec, err = decimal.StringToDecimalScale(v, scale)
default:
return res, fmt.Errorf("unknown value for decimal: %T %#v", v, v)
}
if err != nil {
return res, err
}
dec.SetPrec(prec)
var length byte
switch {
case prec <= 9:
length = 4
case prec <= 19:
length = 8
case prec <= 28:
length = 12
default:
length = 16
}
buf := make([]byte, length+1)
// first byte length written by typeInfo.writer
res.ti.Size = int(length) + 1
// second byte sign
if !dec.IsPositive() {
buf[0] = 0
} else {
buf[0] = 1
}
ub := dec.UnscaledBytes()
l := len(ub)
if l > int(length) {
err = fmt.Errorf("decimal out of range: %s", dec)
return res, err
}
// reverse the bytes
for i, j := 1, l-1; j >= 0; i, j = i+1, j-1 {
buf[i] = ub[j]
}
res.buffer = buf
case typeBigVarBin, typeBigBinary:
switch val := val.(type) {
case []byte:
res.ti.Size = len(val)
res.buffer = val
default:
err = fmt.Errorf("mssql: invalid type for Binary column: %T %s", val, val)
return
}
case typeGuid:
switch val := val.(type) {
case []byte:
res.ti.Size = len(val)
res.buffer = val
default:
err = fmt.Errorf("mssql: invalid type for Guid column: %T %s", val, val)
return
}
default:
err = fmt.Errorf("mssql: type %x not implemented", col.ti.TypeId)
}
return
}
func (b *Bulk) dlogf(format string, v ...interface{}) {
if b.Debug {
b.cn.sess.log.Printf(format, v...)
}
}
-93
View File
@@ -1,93 +0,0 @@
package mssql
import (
"context"
"database/sql/driver"
"encoding/json"
"errors"
)
type copyin struct {
cn *Conn
bulkcopy *Bulk
closed bool
}
type serializableBulkConfig struct {
TableName string
ColumnsName []string
Options BulkOptions
}
func (d *Driver) OpenConnection(dsn string) (*Conn, error) {
return d.open(context.Background(), dsn)
}
func (c *Conn) prepareCopyIn(ctx context.Context, query string) (_ driver.Stmt, err error) {
config_json := query[11:]
bulkconfig := serializableBulkConfig{}
err = json.Unmarshal([]byte(config_json), &bulkconfig)
if err != nil {
return
}
bulkcopy := c.CreateBulkContext(ctx, bulkconfig.TableName, bulkconfig.ColumnsName)
bulkcopy.Options = bulkconfig.Options
ci := &copyin{
cn: c,
bulkcopy: bulkcopy,
}
return ci, nil
}
func CopyIn(table string, options BulkOptions, columns ...string) string {
bulkconfig := &serializableBulkConfig{TableName: table, Options: options, ColumnsName: columns}
config_json, err := json.Marshal(bulkconfig)
if err != nil {
panic(err)
}
stmt := "INSERTBULK " + string(config_json)
return stmt
}
func (ci *copyin) NumInput() int {
return -1
}
func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) {
panic("should never be called")
}
func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
if ci.closed {
return nil, errors.New("copyin query is closed")
}
if len(v) == 0 {
rowCount, err := ci.bulkcopy.Done()
ci.closed = true
return driver.RowsAffected(rowCount), err
}
t := make([]interface{}, len(v))
for i, val := range v {
t[i] = val
}
err = ci.bulkcopy.AddRow(t)
if err != nil {
return
}
return driver.RowsAffected(0), nil
}
func (ci *copyin) Close() (err error) {
return nil
}
-456
View File
@@ -1,456 +0,0 @@
package mssql
import (
"fmt"
"net"
"net/url"
"os"
"strconv"
"strings"
"time"
"unicode"
)
type connectParams struct {
logFlags uint64
port uint64
host string
instance string
database string
user string
password string
dial_timeout time.Duration
conn_timeout time.Duration
keepAlive time.Duration
encrypt bool
disableEncryption bool
trustServerCertificate bool
certificate string
hostInCertificate string
hostInCertificateProvided bool
serverSPN string
workstation string
appname string
typeFlags uint8
failOverPartner string
failOverPort uint64
packetSize uint16
}
func parseConnectParams(dsn string) (connectParams, error) {
var p connectParams
var params map[string]string
if strings.HasPrefix(dsn, "odbc:") {
parameters, err := splitConnectionStringOdbc(dsn[len("odbc:"):])
if err != nil {
return p, err
}
params = parameters
} else if strings.HasPrefix(dsn, "sqlserver://") {
parameters, err := splitConnectionStringURL(dsn)
if err != nil {
return p, err
}
params = parameters
} else {
params = splitConnectionString(dsn)
}
strlog, ok := params["log"]
if ok {
var err error
p.logFlags, err = strconv.ParseUint(strlog, 10, 64)
if err != nil {
return p, fmt.Errorf("Invalid log parameter '%s': %s", strlog, err.Error())
}
}
server := params["server"]
parts := strings.SplitN(server, `\`, 2)
p.host = parts[0]
if p.host == "." || strings.ToUpper(p.host) == "(LOCAL)" || p.host == "" {
p.host = "localhost"
}
if len(parts) > 1 {
p.instance = parts[1]
}
p.database = params["database"]
p.user = params["user id"]
p.password = params["password"]
p.port = 1433
strport, ok := params["port"]
if ok {
var err error
p.port, err = strconv.ParseUint(strport, 10, 16)
if err != nil {
f := "Invalid tcp port '%v': %v"
return p, fmt.Errorf(f, strport, err.Error())
}
}
// https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option
// Default packet size remains at 4096 bytes
p.packetSize = 4096
strpsize, ok := params["packet size"]
if ok {
var err error
psize, err := strconv.ParseUint(strpsize, 0, 16)
if err != nil {
f := "Invalid packet size '%v': %v"
return p, fmt.Errorf(f, strpsize, err.Error())
}
// Ensure packet size falls within the TDS protocol range of 512 to 32767 bytes
// NOTE: Encrypted connections have a maximum size of 16383 bytes. If you request
// a higher packet size, the server will respond with an ENVCHANGE request to
// alter the packet size to 16383 bytes.
p.packetSize = uint16(psize)
if p.packetSize < 512 {
p.packetSize = 512
} else if p.packetSize > 32767 {
p.packetSize = 32767
}
}
// https://msdn.microsoft.com/en-us/library/dd341108.aspx
//
// Do not set a connection timeout. Use Context to manage such things.
// Default to zero, but still allow it to be set.
if strconntimeout, ok := params["connection timeout"]; ok {
timeout, err := strconv.ParseUint(strconntimeout, 10, 64)
if err != nil {
f := "Invalid connection timeout '%v': %v"
return p, fmt.Errorf(f, strconntimeout, err.Error())
}
p.conn_timeout = time.Duration(timeout) * time.Second
}
p.dial_timeout = 15 * time.Second
if strdialtimeout, ok := params["dial timeout"]; ok {
timeout, err := strconv.ParseUint(strdialtimeout, 10, 64)
if err != nil {
f := "Invalid dial timeout '%v': %v"
return p, fmt.Errorf(f, strdialtimeout, err.Error())
}
p.dial_timeout = time.Duration(timeout) * time.Second
}
// default keep alive should be 30 seconds according to spec:
// https://msdn.microsoft.com/en-us/library/dd341108.aspx
p.keepAlive = 30 * time.Second
if keepAlive, ok := params["keepalive"]; ok {
timeout, err := strconv.ParseUint(keepAlive, 10, 64)
if err != nil {
f := "Invalid keepAlive value '%s': %s"
return p, fmt.Errorf(f, keepAlive, err.Error())
}
p.keepAlive = time.Duration(timeout) * time.Second
}
encrypt, ok := params["encrypt"]
if ok {
if strings.EqualFold(encrypt, "DISABLE") {
p.disableEncryption = true
} else {
var err error
p.encrypt, err = strconv.ParseBool(encrypt)
if err != nil {
f := "Invalid encrypt '%s': %s"
return p, fmt.Errorf(f, encrypt, err.Error())
}
}
} else {
p.trustServerCertificate = true
}
trust, ok := params["trustservercertificate"]
if ok {
var err error
p.trustServerCertificate, err = strconv.ParseBool(trust)
if err != nil {
f := "Invalid trust server certificate '%s': %s"
return p, fmt.Errorf(f, trust, err.Error())
}
}
p.certificate = params["certificate"]
p.hostInCertificate, ok = params["hostnameincertificate"]
if ok {
p.hostInCertificateProvided = true
} else {
p.hostInCertificate = p.host
p.hostInCertificateProvided = false
}
serverSPN, ok := params["serverspn"]
if ok {
p.serverSPN = serverSPN
} else {
p.serverSPN = fmt.Sprintf("MSSQLSvc/%s:%d", p.host, p.port)
}
workstation, ok := params["workstation id"]
if ok {
p.workstation = workstation
} else {
workstation, err := os.Hostname()
if err == nil {
p.workstation = workstation
}
}
appname, ok := params["app name"]
if !ok {
appname = "go-mssqldb"
}
p.appname = appname
appintent, ok := params["applicationintent"]
if ok {
if appintent == "ReadOnly" {
if p.database == "" {
return p, fmt.Errorf("Database must be specified when ApplicationIntent is ReadOnly")
}
p.typeFlags |= fReadOnlyIntent
}
}
failOverPartner, ok := params["failoverpartner"]
if ok {
p.failOverPartner = failOverPartner
}
failOverPort, ok := params["failoverport"]
if ok {
var err error
p.failOverPort, err = strconv.ParseUint(failOverPort, 0, 16)
if err != nil {
f := "Invalid tcp port '%v': %v"
return p, fmt.Errorf(f, failOverPort, err.Error())
}
}
return p, nil
}
func splitConnectionString(dsn string) (res map[string]string) {
res = map[string]string{}
parts := strings.Split(dsn, ";")
for _, part := range parts {
if len(part) == 0 {
continue
}
lst := strings.SplitN(part, "=", 2)
name := strings.TrimSpace(strings.ToLower(lst[0]))
if len(name) == 0 {
continue
}
var value string = ""
if len(lst) > 1 {
value = strings.TrimSpace(lst[1])
}
res[name] = value
}
return res
}
// Splits a URL of the form sqlserver://username:password@host/instance?param1=value&param2=value
func splitConnectionStringURL(dsn string) (map[string]string, error) {
res := map[string]string{}
u, err := url.Parse(dsn)
if err != nil {
return res, err
}
if u.Scheme != "sqlserver" {
return res, fmt.Errorf("scheme %s is not recognized", u.Scheme)
}
if u.User != nil {
res["user id"] = u.User.Username()
p, exists := u.User.Password()
if exists {
res["password"] = p
}
}
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
host = u.Host
}
if len(u.Path) > 0 {
res["server"] = host + "\\" + u.Path[1:]
} else {
res["server"] = host
}
if len(port) > 0 {
res["port"] = port
}
query := u.Query()
for k, v := range query {
if len(v) > 1 {
return res, fmt.Errorf("key %s provided more than once", k)
}
res[strings.ToLower(k)] = v[0]
}
return res, nil
}
// Splits a URL in the ODBC format
func splitConnectionStringOdbc(dsn string) (map[string]string, error) {
res := map[string]string{}
type parserState int
const (
// Before the start of a key
parserStateBeforeKey parserState = iota
// Inside a key
parserStateKey
// Beginning of a value. May be bare or braced
parserStateBeginValue
// Inside a bare value
parserStateBareValue
// Inside a braced value
parserStateBracedValue
// A closing brace inside a braced value.
// May be the end of the value or an escaped closing brace, depending on the next character
parserStateBracedValueClosingBrace
// After a value. Next character should be a semicolon or whitespace.
parserStateEndValue
)
var state = parserStateBeforeKey
var key string
var value string
for i, c := range dsn {
switch state {
case parserStateBeforeKey:
switch {
case c == '=':
return res, fmt.Errorf("Unexpected character = at index %d. Expected start of key or semi-colon or whitespace.", i)
case !unicode.IsSpace(c) && c != ';':
state = parserStateKey
key += string(c)
}
case parserStateKey:
switch c {
case '=':
key = normalizeOdbcKey(key)
state = parserStateBeginValue
case ';':
// Key without value
key = normalizeOdbcKey(key)
res[key] = value
key = ""
value = ""
state = parserStateBeforeKey
default:
key += string(c)
}
case parserStateBeginValue:
switch {
case c == '{':
state = parserStateBracedValue
case c == ';':
// Empty value
res[key] = value
key = ""
state = parserStateBeforeKey
case unicode.IsSpace(c):
// Ignore whitespace
default:
state = parserStateBareValue
value += string(c)
}
case parserStateBareValue:
if c == ';' {
res[key] = strings.TrimRightFunc(value, unicode.IsSpace)
key = ""
value = ""
state = parserStateBeforeKey
} else {
value += string(c)
}
case parserStateBracedValue:
if c == '}' {
state = parserStateBracedValueClosingBrace
} else {
value += string(c)
}
case parserStateBracedValueClosingBrace:
if c == '}' {
// Escaped closing brace
value += string(c)
state = parserStateBracedValue
continue
}
// End of braced value
res[key] = value
key = ""
value = ""
// This character is the first character past the end,
// so it needs to be parsed like the parserStateEndValue state.
state = parserStateEndValue
switch {
case c == ';':
state = parserStateBeforeKey
case unicode.IsSpace(c):
// Ignore whitespace
default:
return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i)
}
case parserStateEndValue:
switch {
case c == ';':
state = parserStateBeforeKey
case unicode.IsSpace(c):
// Ignore whitespace
default:
return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i)
}
}
}
switch state {
case parserStateBeforeKey: // Okay
case parserStateKey: // Unfinished key. Treat as key without value.
key = normalizeOdbcKey(key)
res[key] = value
case parserStateBeginValue: // Empty value
res[key] = value
case parserStateBareValue:
res[key] = strings.TrimRightFunc(value, unicode.IsSpace)
case parserStateBracedValue:
return res, fmt.Errorf("Unexpected end of braced value at index %d.", len(dsn))
case parserStateBracedValueClosingBrace: // End of braced value
res[key] = value
case parserStateEndValue: // Okay
}
return res, nil
}
// Normalizes the given string as an ODBC-format key
func normalizeOdbcKey(s string) string {
return strings.ToLower(strings.TrimRightFunc(s, unicode.IsSpace))
}
-306
View File
@@ -1,306 +0,0 @@
package mssql
import "errors"
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Type conversions for Scan.
// This file was imported from database.sql.convert for go 1.10.3 with minor modifications to get
// convertAssign function
// This function is used internally by sql to convert values during call to Scan, we need same
// logic to return values for OUTPUT parameters.
// TODO: sql library should instead expose function defaultCheckNamedValue to be callable by drivers
import (
"database/sql"
"database/sql/driver"
"fmt"
"reflect"
"strconv"
"time"
)
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
func convertAssign(dest, src interface{}) error {
// Common cases, without reflect.
switch s := src.(type) {
case string:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = s
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s)
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = append((*d)[:0], s...)
return nil
}
case []byte:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = string(s)
return nil
case *interface{}:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = s
return nil
}
case time.Time:
switch d := dest.(type) {
case *time.Time:
*d = s
return nil
case *string:
*d = s.Format(time.RFC3339Nano)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s.Format(time.RFC3339Nano))
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = s.AppendFormat((*d)[:0], time.RFC3339Nano)
return nil
}
case nil:
switch d := dest.(type) {
case *interface{}:
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = nil
return nil
}
}
var sv reflect.Value
switch d := dest.(type) {
case *string:
sv = reflect.ValueOf(src)
switch sv.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
*d = asString(src)
return nil
}
case *[]byte:
sv = reflect.ValueOf(src)
if b, ok := asBytes(nil, sv); ok {
*d = b
return nil
}
case *sql.RawBytes:
sv = reflect.ValueOf(src)
if b, ok := asBytes([]byte(*d)[:0], sv); ok {
*d = sql.RawBytes(b)
return nil
}
case *bool:
bv, err := driver.Bool.ConvertValue(src)
if err == nil {
*d = bv.(bool)
}
return err
case *interface{}:
*d = src
return nil
}
if scanner, ok := dest.(sql.Scanner); ok {
return scanner.Scan(src)
}
dpv := reflect.ValueOf(dest)
if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
if dpv.IsNil() {
return errNilPtr
}
if !sv.IsValid() {
sv = reflect.ValueOf(src)
}
dv := reflect.Indirect(dpv)
if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
switch b := src.(type) {
case []byte:
dv.Set(reflect.ValueOf(cloneBytes(b)))
default:
dv.Set(sv)
}
return nil
}
if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
dv.Set(sv.Convert(dv.Type()))
return nil
}
// The following conversions use a string value as an intermediate representation
// to convert between various numeric types.
//
// This also allows scanning into user defined types such as "type Int int64".
// For symmetry, also check for string destination types.
switch dv.Kind() {
case reflect.Ptr:
if src == nil {
dv.Set(reflect.Zero(dv.Type()))
return nil
} else {
dv.Set(reflect.New(dv.Type().Elem()))
return convertAssign(dv.Interface(), src)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s := asString(src)
i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetInt(i64)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
s := asString(src)
u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetUint(u64)
return nil
case reflect.Float32, reflect.Float64:
s := asString(src)
f64, err := strconv.ParseFloat(s, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetFloat(f64)
return nil
case reflect.String:
switch v := src.(type) {
case string:
dv.SetString(v)
return nil
case []byte:
dv.SetString(string(v))
return nil
}
}
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
}
func strconvErr(err error) error {
if ne, ok := err.(*strconv.NumError); ok {
return ne.Err
}
return err
}
func cloneBytes(b []byte) []byte {
if b == nil {
return nil
} else {
c := make([]byte, len(b))
copy(c, b)
return c
}
}
func asString(src interface{}) string {
switch v := src.(type) {
case string:
return v
case []byte:
return string(v)
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
case reflect.Float32:
return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
return fmt.Sprintf("%v", src)
}
func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.AppendInt(buf, rv.Int(), 10), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.AppendUint(buf, rv.Uint(), 10), true
case reflect.Float32:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
case reflect.Float64:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
case reflect.Bool:
return strconv.AppendBool(buf, rv.Bool()), true
case reflect.String:
s := rv.String()
return append(buf, s...), true
}
return
}
-14
View File
@@ -1,14 +0,0 @@
// package mssql implements the TDS protocol used to connect to MS SQL Server (sqlserver)
// database servers.
//
// This package registers the driver:
// sqlserver: uses native "@" parameter placeholder names and does no pre-processing.
//
// If the ordinal position is used for query parameters, identifiers will be named
// "@p1", "@p2", ... "@pN".
//
// Please refer to the README for the format of the DSN. There are multiple DSN
// formats accepted: ADO style, ODBC style, and URL style. The following is an
// example of a URL style DSN:
// sqlserver://sa:mypass@localhost:1234?database=master&connection+timeout=30
package mssql
-73
View File
@@ -1,73 +0,0 @@
package mssql
import (
"fmt"
)
// Error represents an SQL Server error. This
// type includes methods for reading the contents
// of the struct, which allows calling programs
// to check for specific error conditions without
// having to import this package directly.
type Error struct {
Number int32
State uint8
Class uint8
Message string
ServerName string
ProcName string
LineNo int32
}
func (e Error) Error() string {
return "mssql: " + e.Message
}
// SQLErrorNumber returns the SQL Server error number.
func (e Error) SQLErrorNumber() int32 {
return e.Number
}
func (e Error) SQLErrorState() uint8 {
return e.State
}
func (e Error) SQLErrorClass() uint8 {
return e.Class
}
func (e Error) SQLErrorMessage() string {
return e.Message
}
func (e Error) SQLErrorServerName() string {
return e.ServerName
}
func (e Error) SQLErrorProcName() string {
return e.ProcName
}
func (e Error) SQLErrorLineNo() int32 {
return e.LineNo
}
type StreamError struct {
Message string
}
func (e StreamError) Error() string {
return e.Message
}
func streamErrorf(format string, v ...interface{}) StreamError {
return StreamError{"Invalid TDS stream: " + fmt.Sprintf(format, v...)}
}
func badStreamPanic(err error) {
panic(err)
}
func badStreamPanicf(format string, v ...interface{}) {
panic(streamErrorf(format, v...))
}
-8
View File
@@ -1,8 +0,0 @@
module github.com/denisenkom/go-mssqldb
go 1.11
require (
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
)
-5
View File
@@ -1,5 +0,0 @@
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-113
View File
@@ -1,113 +0,0 @@
package cp
type charsetMap struct {
sb [256]rune // single byte runes, -1 for a double byte character lead byte
db map[int]rune // double byte runes
}
func collation2charset(col Collation) *charsetMap {
// http://msdn.microsoft.com/en-us/library/ms144250.aspx
// http://msdn.microsoft.com/en-us/library/ms144250(v=sql.105).aspx
switch col.SortId {
case 30, 31, 32, 33, 34:
return cp437
case 40, 41, 42, 44, 49, 55, 56, 57, 58, 59, 60, 61:
return cp850
case 50, 51, 52, 53, 54, 71, 72, 73, 74, 75:
return cp1252
case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96:
return cp1250
case 104, 105, 106, 107, 108:
return cp1251
case 112, 113, 114, 121, 124:
return cp1253
case 128, 129, 130:
return cp1254
case 136, 137, 138:
return cp1255
case 144, 145, 146:
return cp1256
case 152, 153, 154, 155, 156, 157, 158, 159, 160:
return cp1257
case 183, 184, 185, 186:
return cp1252
case 192, 193:
return cp932
case 194, 195:
return cp949
case 196, 197:
return cp950
case 198, 199:
return cp936
case 200:
return cp932
case 201:
return cp949
case 202:
return cp950
case 203:
return cp936
case 204, 205, 206:
return cp874
case 210, 211, 212, 213, 214, 215, 216, 217:
return cp1252
}
// http://technet.microsoft.com/en-us/library/aa176553(v=sql.80).aspx
switch col.getLcid() {
case 0x001e, 0x041e:
return cp874
case 0x0411, 0x10411:
return cp932
case 0x0804, 0x1004, 0x20804:
return cp936
case 0x0012, 0x0412:
return cp949
case 0x0404, 0x1404, 0x0c04, 0x7c04, 0x30404:
return cp950
case 0x041c, 0x041a, 0x0405, 0x040e, 0x104e, 0x0415, 0x0418, 0x041b, 0x0424, 0x1040e:
return cp1250
case 0x0423, 0x0402, 0x042f, 0x0419, 0x081a, 0x0c1a, 0x0422, 0x043f, 0x0444, 0x082c:
return cp1251
case 0x0408:
return cp1253
case 0x041f, 0x042c, 0x0443:
return cp1254
case 0x040d:
return cp1255
case 0x0401, 0x0801, 0xc01, 0x1001, 0x1401, 0x1801, 0x1c01, 0x2001, 0x2401, 0x2801, 0x2c01, 0x3001, 0x3401, 0x3801, 0x3c01, 0x4001, 0x0429, 0x0420:
return cp1256
case 0x0425, 0x0426, 0x0427, 0x0827:
return cp1257
case 0x042a:
return cp1258
case 0x0439, 0x045a, 0x0465:
return nil
}
return cp1252
}
func CharsetToUTF8(col Collation, s []byte) string {
cm := collation2charset(col)
if cm == nil {
return string(s)
}
buf := make([]rune, 0, len(s))
for i := 0; i < len(s); i++ {
ch := cm.sb[s[i]]
if ch == -1 {
if i+1 == len(s) {
ch = 0xfffd
} else {
n := int(s[i+1]) + (int(s[i]) << 8)
i++
var ok bool
ch, ok = cm.db[n]
if !ok {
ch = 0xfffd
}
}
}
buf = append(buf, ch)
}
return string(buf)
}
-20
View File
@@ -1,20 +0,0 @@
package cp
// http://msdn.microsoft.com/en-us/library/dd340437.aspx
type Collation struct {
LcidAndFlags uint32
SortId uint8
}
func (c Collation) getLcid() uint32 {
return c.LcidAndFlags & 0x000fffff
}
func (c Collation) getFlags() uint32 {
return (c.LcidAndFlags & 0x0ff00000) >> 20
}
func (c Collation) getVersion() uint32 {
return (c.LcidAndFlags & 0xf0000000) >> 28
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1250 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0xFFFD, //UNDEFINED
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0xFFFD, //UNDEFINED
0x2030, //PER MILLE SIGN
0x0160, //LATIN CAPITAL LETTER S WITH CARON
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x015A, //LATIN CAPITAL LETTER S WITH ACUTE
0x0164, //LATIN CAPITAL LETTER T WITH CARON
0x017D, //LATIN CAPITAL LETTER Z WITH CARON
0x0179, //LATIN CAPITAL LETTER Z WITH ACUTE
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0xFFFD, //UNDEFINED
0x2122, //TRADE MARK SIGN
0x0161, //LATIN SMALL LETTER S WITH CARON
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x015B, //LATIN SMALL LETTER S WITH ACUTE
0x0165, //LATIN SMALL LETTER T WITH CARON
0x017E, //LATIN SMALL LETTER Z WITH CARON
0x017A, //LATIN SMALL LETTER Z WITH ACUTE
0x00A0, //NO-BREAK SPACE
0x02C7, //CARON
0x02D8, //BREVE
0x0141, //LATIN CAPITAL LETTER L WITH STROKE
0x00A4, //CURRENCY SIGN
0x0104, //LATIN CAPITAL LETTER A WITH OGONEK
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x015E, //LATIN CAPITAL LETTER S WITH CEDILLA
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x017B, //LATIN CAPITAL LETTER Z WITH DOT ABOVE
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x02DB, //OGONEK
0x0142, //LATIN SMALL LETTER L WITH STROKE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x0105, //LATIN SMALL LETTER A WITH OGONEK
0x015F, //LATIN SMALL LETTER S WITH CEDILLA
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x013D, //LATIN CAPITAL LETTER L WITH CARON
0x02DD, //DOUBLE ACUTE ACCENT
0x013E, //LATIN SMALL LETTER L WITH CARON
0x017C, //LATIN SMALL LETTER Z WITH DOT ABOVE
0x0154, //LATIN CAPITAL LETTER R WITH ACUTE
0x00C1, //LATIN CAPITAL LETTER A WITH ACUTE
0x00C2, //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x0102, //LATIN CAPITAL LETTER A WITH BREVE
0x00C4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x0139, //LATIN CAPITAL LETTER L WITH ACUTE
0x0106, //LATIN CAPITAL LETTER C WITH ACUTE
0x00C7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x010C, //LATIN CAPITAL LETTER C WITH CARON
0x00C9, //LATIN CAPITAL LETTER E WITH ACUTE
0x0118, //LATIN CAPITAL LETTER E WITH OGONEK
0x00CB, //LATIN CAPITAL LETTER E WITH DIAERESIS
0x011A, //LATIN CAPITAL LETTER E WITH CARON
0x00CD, //LATIN CAPITAL LETTER I WITH ACUTE
0x00CE, //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x010E, //LATIN CAPITAL LETTER D WITH CARON
0x0110, //LATIN CAPITAL LETTER D WITH STROKE
0x0143, //LATIN CAPITAL LETTER N WITH ACUTE
0x0147, //LATIN CAPITAL LETTER N WITH CARON
0x00D3, //LATIN CAPITAL LETTER O WITH ACUTE
0x00D4, //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x0150, //LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
0x00D6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7, //MULTIPLICATION SIGN
0x0158, //LATIN CAPITAL LETTER R WITH CARON
0x016E, //LATIN CAPITAL LETTER U WITH RING ABOVE
0x00DA, //LATIN CAPITAL LETTER U WITH ACUTE
0x0170, //LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
0x00DC, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x00DD, //LATIN CAPITAL LETTER Y WITH ACUTE
0x0162, //LATIN CAPITAL LETTER T WITH CEDILLA
0x00DF, //LATIN SMALL LETTER SHARP S
0x0155, //LATIN SMALL LETTER R WITH ACUTE
0x00E1, //LATIN SMALL LETTER A WITH ACUTE
0x00E2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x0103, //LATIN SMALL LETTER A WITH BREVE
0x00E4, //LATIN SMALL LETTER A WITH DIAERESIS
0x013A, //LATIN SMALL LETTER L WITH ACUTE
0x0107, //LATIN SMALL LETTER C WITH ACUTE
0x00E7, //LATIN SMALL LETTER C WITH CEDILLA
0x010D, //LATIN SMALL LETTER C WITH CARON
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x0119, //LATIN SMALL LETTER E WITH OGONEK
0x00EB, //LATIN SMALL LETTER E WITH DIAERESIS
0x011B, //LATIN SMALL LETTER E WITH CARON
0x00ED, //LATIN SMALL LETTER I WITH ACUTE
0x00EE, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x010F, //LATIN SMALL LETTER D WITH CARON
0x0111, //LATIN SMALL LETTER D WITH STROKE
0x0144, //LATIN SMALL LETTER N WITH ACUTE
0x0148, //LATIN SMALL LETTER N WITH CARON
0x00F3, //LATIN SMALL LETTER O WITH ACUTE
0x00F4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x0151, //LATIN SMALL LETTER O WITH DOUBLE ACUTE
0x00F6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00F7, //DIVISION SIGN
0x0159, //LATIN SMALL LETTER R WITH CARON
0x016F, //LATIN SMALL LETTER U WITH RING ABOVE
0x00FA, //LATIN SMALL LETTER U WITH ACUTE
0x0171, //LATIN SMALL LETTER U WITH DOUBLE ACUTE
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x00FD, //LATIN SMALL LETTER Y WITH ACUTE
0x0163, //LATIN SMALL LETTER T WITH CEDILLA
0x02D9, //DOT ABOVE
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1251 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x0402, //CYRILLIC CAPITAL LETTER DJE
0x0403, //CYRILLIC CAPITAL LETTER GJE
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0453, //CYRILLIC SMALL LETTER GJE
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x20AC, //EURO SIGN
0x2030, //PER MILLE SIGN
0x0409, //CYRILLIC CAPITAL LETTER LJE
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x040A, //CYRILLIC CAPITAL LETTER NJE
0x040C, //CYRILLIC CAPITAL LETTER KJE
0x040B, //CYRILLIC CAPITAL LETTER TSHE
0x040F, //CYRILLIC CAPITAL LETTER DZHE
0x0452, //CYRILLIC SMALL LETTER DJE
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0xFFFD, //UNDEFINED
0x2122, //TRADE MARK SIGN
0x0459, //CYRILLIC SMALL LETTER LJE
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x045A, //CYRILLIC SMALL LETTER NJE
0x045C, //CYRILLIC SMALL LETTER KJE
0x045B, //CYRILLIC SMALL LETTER TSHE
0x045F, //CYRILLIC SMALL LETTER DZHE
0x00A0, //NO-BREAK SPACE
0x040E, //CYRILLIC CAPITAL LETTER SHORT U
0x045E, //CYRILLIC SMALL LETTER SHORT U
0x0408, //CYRILLIC CAPITAL LETTER JE
0x00A4, //CURRENCY SIGN
0x0490, //CYRILLIC CAPITAL LETTER GHE WITH UPTURN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x0401, //CYRILLIC CAPITAL LETTER IO
0x00A9, //COPYRIGHT SIGN
0x0404, //CYRILLIC CAPITAL LETTER UKRAINIAN IE
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x0407, //CYRILLIC CAPITAL LETTER YI
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x0406, //CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
0x0456, //CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
0x0491, //CYRILLIC SMALL LETTER GHE WITH UPTURN
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x0451, //CYRILLIC SMALL LETTER IO
0x2116, //NUMERO SIGN
0x0454, //CYRILLIC SMALL LETTER UKRAINIAN IE
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x0458, //CYRILLIC SMALL LETTER JE
0x0405, //CYRILLIC CAPITAL LETTER DZE
0x0455, //CYRILLIC SMALL LETTER DZE
0x0457, //CYRILLIC SMALL LETTER YI
0x0410, //CYRILLIC CAPITAL LETTER A
0x0411, //CYRILLIC CAPITAL LETTER BE
0x0412, //CYRILLIC CAPITAL LETTER VE
0x0413, //CYRILLIC CAPITAL LETTER GHE
0x0414, //CYRILLIC CAPITAL LETTER DE
0x0415, //CYRILLIC CAPITAL LETTER IE
0x0416, //CYRILLIC CAPITAL LETTER ZHE
0x0417, //CYRILLIC CAPITAL LETTER ZE
0x0418, //CYRILLIC CAPITAL LETTER I
0x0419, //CYRILLIC CAPITAL LETTER SHORT I
0x041A, //CYRILLIC CAPITAL LETTER KA
0x041B, //CYRILLIC CAPITAL LETTER EL
0x041C, //CYRILLIC CAPITAL LETTER EM
0x041D, //CYRILLIC CAPITAL LETTER EN
0x041E, //CYRILLIC CAPITAL LETTER O
0x041F, //CYRILLIC CAPITAL LETTER PE
0x0420, //CYRILLIC CAPITAL LETTER ER
0x0421, //CYRILLIC CAPITAL LETTER ES
0x0422, //CYRILLIC CAPITAL LETTER TE
0x0423, //CYRILLIC CAPITAL LETTER U
0x0424, //CYRILLIC CAPITAL LETTER EF
0x0425, //CYRILLIC CAPITAL LETTER HA
0x0426, //CYRILLIC CAPITAL LETTER TSE
0x0427, //CYRILLIC CAPITAL LETTER CHE
0x0428, //CYRILLIC CAPITAL LETTER SHA
0x0429, //CYRILLIC CAPITAL LETTER SHCHA
0x042A, //CYRILLIC CAPITAL LETTER HARD SIGN
0x042B, //CYRILLIC CAPITAL LETTER YERU
0x042C, //CYRILLIC CAPITAL LETTER SOFT SIGN
0x042D, //CYRILLIC CAPITAL LETTER E
0x042E, //CYRILLIC CAPITAL LETTER YU
0x042F, //CYRILLIC CAPITAL LETTER YA
0x0430, //CYRILLIC SMALL LETTER A
0x0431, //CYRILLIC SMALL LETTER BE
0x0432, //CYRILLIC SMALL LETTER VE
0x0433, //CYRILLIC SMALL LETTER GHE
0x0434, //CYRILLIC SMALL LETTER DE
0x0435, //CYRILLIC SMALL LETTER IE
0x0436, //CYRILLIC SMALL LETTER ZHE
0x0437, //CYRILLIC SMALL LETTER ZE
0x0438, //CYRILLIC SMALL LETTER I
0x0439, //CYRILLIC SMALL LETTER SHORT I
0x043A, //CYRILLIC SMALL LETTER KA
0x043B, //CYRILLIC SMALL LETTER EL
0x043C, //CYRILLIC SMALL LETTER EM
0x043D, //CYRILLIC SMALL LETTER EN
0x043E, //CYRILLIC SMALL LETTER O
0x043F, //CYRILLIC SMALL LETTER PE
0x0440, //CYRILLIC SMALL LETTER ER
0x0441, //CYRILLIC SMALL LETTER ES
0x0442, //CYRILLIC SMALL LETTER TE
0x0443, //CYRILLIC SMALL LETTER U
0x0444, //CYRILLIC SMALL LETTER EF
0x0445, //CYRILLIC SMALL LETTER HA
0x0446, //CYRILLIC SMALL LETTER TSE
0x0447, //CYRILLIC SMALL LETTER CHE
0x0448, //CYRILLIC SMALL LETTER SHA
0x0449, //CYRILLIC SMALL LETTER SHCHA
0x044A, //CYRILLIC SMALL LETTER HARD SIGN
0x044B, //CYRILLIC SMALL LETTER YERU
0x044C, //CYRILLIC SMALL LETTER SOFT SIGN
0x044D, //CYRILLIC SMALL LETTER E
0x044E, //CYRILLIC SMALL LETTER YU
0x044F, //CYRILLIC SMALL LETTER YA
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1252 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x02C6, //MODIFIER LETTER CIRCUMFLEX ACCENT
0x2030, //PER MILLE SIGN
0x0160, //LATIN CAPITAL LETTER S WITH CARON
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x0152, //LATIN CAPITAL LIGATURE OE
0xFFFD, //UNDEFINED
0x017D, //LATIN CAPITAL LETTER Z WITH CARON
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0x02DC, //SMALL TILDE
0x2122, //TRADE MARK SIGN
0x0161, //LATIN SMALL LETTER S WITH CARON
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x0153, //LATIN SMALL LIGATURE OE
0xFFFD, //UNDEFINED
0x017E, //LATIN SMALL LETTER Z WITH CARON
0x0178, //LATIN CAPITAL LETTER Y WITH DIAERESIS
0x00A0, //NO-BREAK SPACE
0x00A1, //INVERTED EXCLAMATION MARK
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x00AA, //FEMININE ORDINAL INDICATOR
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00AF, //MACRON
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x00B9, //SUPERSCRIPT ONE
0x00BA, //MASCULINE ORDINAL INDICATOR
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x00BF, //INVERTED QUESTION MARK
0x00C0, //LATIN CAPITAL LETTER A WITH GRAVE
0x00C1, //LATIN CAPITAL LETTER A WITH ACUTE
0x00C2, //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x00C3, //LATIN CAPITAL LETTER A WITH TILDE
0x00C4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x00C6, //LATIN CAPITAL LETTER AE
0x00C7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x00C8, //LATIN CAPITAL LETTER E WITH GRAVE
0x00C9, //LATIN CAPITAL LETTER E WITH ACUTE
0x00CA, //LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00CB, //LATIN CAPITAL LETTER E WITH DIAERESIS
0x00CC, //LATIN CAPITAL LETTER I WITH GRAVE
0x00CD, //LATIN CAPITAL LETTER I WITH ACUTE
0x00CE, //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00CF, //LATIN CAPITAL LETTER I WITH DIAERESIS
0x00D0, //LATIN CAPITAL LETTER ETH
0x00D1, //LATIN CAPITAL LETTER N WITH TILDE
0x00D2, //LATIN CAPITAL LETTER O WITH GRAVE
0x00D3, //LATIN CAPITAL LETTER O WITH ACUTE
0x00D4, //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x00D5, //LATIN CAPITAL LETTER O WITH TILDE
0x00D6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7, //MULTIPLICATION SIGN
0x00D8, //LATIN CAPITAL LETTER O WITH STROKE
0x00D9, //LATIN CAPITAL LETTER U WITH GRAVE
0x00DA, //LATIN CAPITAL LETTER U WITH ACUTE
0x00DB, //LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00DC, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x00DD, //LATIN CAPITAL LETTER Y WITH ACUTE
0x00DE, //LATIN CAPITAL LETTER THORN
0x00DF, //LATIN SMALL LETTER SHARP S
0x00E0, //LATIN SMALL LETTER A WITH GRAVE
0x00E1, //LATIN SMALL LETTER A WITH ACUTE
0x00E2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00E3, //LATIN SMALL LETTER A WITH TILDE
0x00E4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00E5, //LATIN SMALL LETTER A WITH RING ABOVE
0x00E6, //LATIN SMALL LETTER AE
0x00E7, //LATIN SMALL LETTER C WITH CEDILLA
0x00E8, //LATIN SMALL LETTER E WITH GRAVE
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x00EA, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB, //LATIN SMALL LETTER E WITH DIAERESIS
0x00EC, //LATIN SMALL LETTER I WITH GRAVE
0x00ED, //LATIN SMALL LETTER I WITH ACUTE
0x00EE, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF, //LATIN SMALL LETTER I WITH DIAERESIS
0x00F0, //LATIN SMALL LETTER ETH
0x00F1, //LATIN SMALL LETTER N WITH TILDE
0x00F2, //LATIN SMALL LETTER O WITH GRAVE
0x00F3, //LATIN SMALL LETTER O WITH ACUTE
0x00F4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00F5, //LATIN SMALL LETTER O WITH TILDE
0x00F6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00F7, //DIVISION SIGN
0x00F8, //LATIN SMALL LETTER O WITH STROKE
0x00F9, //LATIN SMALL LETTER U WITH GRAVE
0x00FA, //LATIN SMALL LETTER U WITH ACUTE
0x00FB, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x00FD, //LATIN SMALL LETTER Y WITH ACUTE
0x00FE, //LATIN SMALL LETTER THORN
0x00FF, //LATIN SMALL LETTER Y WITH DIAERESIS
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1253 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0xFFFD, //UNDEFINED
0x2030, //PER MILLE SIGN
0xFFFD, //UNDEFINED
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0xFFFD, //UNDEFINED
0x2122, //TRADE MARK SIGN
0xFFFD, //UNDEFINED
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x00A0, //NO-BREAK SPACE
0x0385, //GREEK DIALYTIKA TONOS
0x0386, //GREEK CAPITAL LETTER ALPHA WITH TONOS
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0xFFFD, //UNDEFINED
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x2015, //HORIZONTAL BAR
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x0384, //GREEK TONOS
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x0388, //GREEK CAPITAL LETTER EPSILON WITH TONOS
0x0389, //GREEK CAPITAL LETTER ETA WITH TONOS
0x038A, //GREEK CAPITAL LETTER IOTA WITH TONOS
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x038C, //GREEK CAPITAL LETTER OMICRON WITH TONOS
0x00BD, //VULGAR FRACTION ONE HALF
0x038E, //GREEK CAPITAL LETTER UPSILON WITH TONOS
0x038F, //GREEK CAPITAL LETTER OMEGA WITH TONOS
0x0390, //GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
0x0391, //GREEK CAPITAL LETTER ALPHA
0x0392, //GREEK CAPITAL LETTER BETA
0x0393, //GREEK CAPITAL LETTER GAMMA
0x0394, //GREEK CAPITAL LETTER DELTA
0x0395, //GREEK CAPITAL LETTER EPSILON
0x0396, //GREEK CAPITAL LETTER ZETA
0x0397, //GREEK CAPITAL LETTER ETA
0x0398, //GREEK CAPITAL LETTER THETA
0x0399, //GREEK CAPITAL LETTER IOTA
0x039A, //GREEK CAPITAL LETTER KAPPA
0x039B, //GREEK CAPITAL LETTER LAMDA
0x039C, //GREEK CAPITAL LETTER MU
0x039D, //GREEK CAPITAL LETTER NU
0x039E, //GREEK CAPITAL LETTER XI
0x039F, //GREEK CAPITAL LETTER OMICRON
0x03A0, //GREEK CAPITAL LETTER PI
0x03A1, //GREEK CAPITAL LETTER RHO
0xFFFD, //UNDEFINED
0x03A3, //GREEK CAPITAL LETTER SIGMA
0x03A4, //GREEK CAPITAL LETTER TAU
0x03A5, //GREEK CAPITAL LETTER UPSILON
0x03A6, //GREEK CAPITAL LETTER PHI
0x03A7, //GREEK CAPITAL LETTER CHI
0x03A8, //GREEK CAPITAL LETTER PSI
0x03A9, //GREEK CAPITAL LETTER OMEGA
0x03AA, //GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
0x03AB, //GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
0x03AC, //GREEK SMALL LETTER ALPHA WITH TONOS
0x03AD, //GREEK SMALL LETTER EPSILON WITH TONOS
0x03AE, //GREEK SMALL LETTER ETA WITH TONOS
0x03AF, //GREEK SMALL LETTER IOTA WITH TONOS
0x03B0, //GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
0x03B1, //GREEK SMALL LETTER ALPHA
0x03B2, //GREEK SMALL LETTER BETA
0x03B3, //GREEK SMALL LETTER GAMMA
0x03B4, //GREEK SMALL LETTER DELTA
0x03B5, //GREEK SMALL LETTER EPSILON
0x03B6, //GREEK SMALL LETTER ZETA
0x03B7, //GREEK SMALL LETTER ETA
0x03B8, //GREEK SMALL LETTER THETA
0x03B9, //GREEK SMALL LETTER IOTA
0x03BA, //GREEK SMALL LETTER KAPPA
0x03BB, //GREEK SMALL LETTER LAMDA
0x03BC, //GREEK SMALL LETTER MU
0x03BD, //GREEK SMALL LETTER NU
0x03BE, //GREEK SMALL LETTER XI
0x03BF, //GREEK SMALL LETTER OMICRON
0x03C0, //GREEK SMALL LETTER PI
0x03C1, //GREEK SMALL LETTER RHO
0x03C2, //GREEK SMALL LETTER FINAL SIGMA
0x03C3, //GREEK SMALL LETTER SIGMA
0x03C4, //GREEK SMALL LETTER TAU
0x03C5, //GREEK SMALL LETTER UPSILON
0x03C6, //GREEK SMALL LETTER PHI
0x03C7, //GREEK SMALL LETTER CHI
0x03C8, //GREEK SMALL LETTER PSI
0x03C9, //GREEK SMALL LETTER OMEGA
0x03CA, //GREEK SMALL LETTER IOTA WITH DIALYTIKA
0x03CB, //GREEK SMALL LETTER UPSILON WITH DIALYTIKA
0x03CC, //GREEK SMALL LETTER OMICRON WITH TONOS
0x03CD, //GREEK SMALL LETTER UPSILON WITH TONOS
0x03CE, //GREEK SMALL LETTER OMEGA WITH TONOS
0xFFFD, //UNDEFINED
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1254 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x02C6, //MODIFIER LETTER CIRCUMFLEX ACCENT
0x2030, //PER MILLE SIGN
0x0160, //LATIN CAPITAL LETTER S WITH CARON
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x0152, //LATIN CAPITAL LIGATURE OE
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0x02DC, //SMALL TILDE
0x2122, //TRADE MARK SIGN
0x0161, //LATIN SMALL LETTER S WITH CARON
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x0153, //LATIN SMALL LIGATURE OE
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x0178, //LATIN CAPITAL LETTER Y WITH DIAERESIS
0x00A0, //NO-BREAK SPACE
0x00A1, //INVERTED EXCLAMATION MARK
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x00AA, //FEMININE ORDINAL INDICATOR
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00AF, //MACRON
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x00B9, //SUPERSCRIPT ONE
0x00BA, //MASCULINE ORDINAL INDICATOR
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x00BF, //INVERTED QUESTION MARK
0x00C0, //LATIN CAPITAL LETTER A WITH GRAVE
0x00C1, //LATIN CAPITAL LETTER A WITH ACUTE
0x00C2, //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x00C3, //LATIN CAPITAL LETTER A WITH TILDE
0x00C4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x00C6, //LATIN CAPITAL LETTER AE
0x00C7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x00C8, //LATIN CAPITAL LETTER E WITH GRAVE
0x00C9, //LATIN CAPITAL LETTER E WITH ACUTE
0x00CA, //LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00CB, //LATIN CAPITAL LETTER E WITH DIAERESIS
0x00CC, //LATIN CAPITAL LETTER I WITH GRAVE
0x00CD, //LATIN CAPITAL LETTER I WITH ACUTE
0x00CE, //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00CF, //LATIN CAPITAL LETTER I WITH DIAERESIS
0x011E, //LATIN CAPITAL LETTER G WITH BREVE
0x00D1, //LATIN CAPITAL LETTER N WITH TILDE
0x00D2, //LATIN CAPITAL LETTER O WITH GRAVE
0x00D3, //LATIN CAPITAL LETTER O WITH ACUTE
0x00D4, //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x00D5, //LATIN CAPITAL LETTER O WITH TILDE
0x00D6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7, //MULTIPLICATION SIGN
0x00D8, //LATIN CAPITAL LETTER O WITH STROKE
0x00D9, //LATIN CAPITAL LETTER U WITH GRAVE
0x00DA, //LATIN CAPITAL LETTER U WITH ACUTE
0x00DB, //LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00DC, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x0130, //LATIN CAPITAL LETTER I WITH DOT ABOVE
0x015E, //LATIN CAPITAL LETTER S WITH CEDILLA
0x00DF, //LATIN SMALL LETTER SHARP S
0x00E0, //LATIN SMALL LETTER A WITH GRAVE
0x00E1, //LATIN SMALL LETTER A WITH ACUTE
0x00E2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00E3, //LATIN SMALL LETTER A WITH TILDE
0x00E4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00E5, //LATIN SMALL LETTER A WITH RING ABOVE
0x00E6, //LATIN SMALL LETTER AE
0x00E7, //LATIN SMALL LETTER C WITH CEDILLA
0x00E8, //LATIN SMALL LETTER E WITH GRAVE
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x00EA, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB, //LATIN SMALL LETTER E WITH DIAERESIS
0x00EC, //LATIN SMALL LETTER I WITH GRAVE
0x00ED, //LATIN SMALL LETTER I WITH ACUTE
0x00EE, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF, //LATIN SMALL LETTER I WITH DIAERESIS
0x011F, //LATIN SMALL LETTER G WITH BREVE
0x00F1, //LATIN SMALL LETTER N WITH TILDE
0x00F2, //LATIN SMALL LETTER O WITH GRAVE
0x00F3, //LATIN SMALL LETTER O WITH ACUTE
0x00F4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00F5, //LATIN SMALL LETTER O WITH TILDE
0x00F6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00F7, //DIVISION SIGN
0x00F8, //LATIN SMALL LETTER O WITH STROKE
0x00F9, //LATIN SMALL LETTER U WITH GRAVE
0x00FA, //LATIN SMALL LETTER U WITH ACUTE
0x00FB, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x0131, //LATIN SMALL LETTER DOTLESS I
0x015F, //LATIN SMALL LETTER S WITH CEDILLA
0x00FF, //LATIN SMALL LETTER Y WITH DIAERESIS
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1255 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x02C6, //MODIFIER LETTER CIRCUMFLEX ACCENT
0x2030, //PER MILLE SIGN
0xFFFD, //UNDEFINED
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0x02DC, //SMALL TILDE
0x2122, //TRADE MARK SIGN
0xFFFD, //UNDEFINED
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x00A0, //NO-BREAK SPACE
0x00A1, //INVERTED EXCLAMATION MARK
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x20AA, //NEW SHEQEL SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x00D7, //MULTIPLICATION SIGN
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00AF, //MACRON
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x00B9, //SUPERSCRIPT ONE
0x00F7, //DIVISION SIGN
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x00BF, //INVERTED QUESTION MARK
0x05B0, //HEBREW POINT SHEVA
0x05B1, //HEBREW POINT HATAF SEGOL
0x05B2, //HEBREW POINT HATAF PATAH
0x05B3, //HEBREW POINT HATAF QAMATS
0x05B4, //HEBREW POINT HIRIQ
0x05B5, //HEBREW POINT TSERE
0x05B6, //HEBREW POINT SEGOL
0x05B7, //HEBREW POINT PATAH
0x05B8, //HEBREW POINT QAMATS
0x05B9, //HEBREW POINT HOLAM
0xFFFD, //UNDEFINED
0x05BB, //HEBREW POINT QUBUTS
0x05BC, //HEBREW POINT DAGESH OR MAPIQ
0x05BD, //HEBREW POINT METEG
0x05BE, //HEBREW PUNCTUATION MAQAF
0x05BF, //HEBREW POINT RAFE
0x05C0, //HEBREW PUNCTUATION PASEQ
0x05C1, //HEBREW POINT SHIN DOT
0x05C2, //HEBREW POINT SIN DOT
0x05C3, //HEBREW PUNCTUATION SOF PASUQ
0x05F0, //HEBREW LIGATURE YIDDISH DOUBLE VAV
0x05F1, //HEBREW LIGATURE YIDDISH VAV YOD
0x05F2, //HEBREW LIGATURE YIDDISH DOUBLE YOD
0x05F3, //HEBREW PUNCTUATION GERESH
0x05F4, //HEBREW PUNCTUATION GERSHAYIM
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x05D0, //HEBREW LETTER ALEF
0x05D1, //HEBREW LETTER BET
0x05D2, //HEBREW LETTER GIMEL
0x05D3, //HEBREW LETTER DALET
0x05D4, //HEBREW LETTER HE
0x05D5, //HEBREW LETTER VAV
0x05D6, //HEBREW LETTER ZAYIN
0x05D7, //HEBREW LETTER HET
0x05D8, //HEBREW LETTER TET
0x05D9, //HEBREW LETTER YOD
0x05DA, //HEBREW LETTER FINAL KAF
0x05DB, //HEBREW LETTER KAF
0x05DC, //HEBREW LETTER LAMED
0x05DD, //HEBREW LETTER FINAL MEM
0x05DE, //HEBREW LETTER MEM
0x05DF, //HEBREW LETTER FINAL NUN
0x05E0, //HEBREW LETTER NUN
0x05E1, //HEBREW LETTER SAMEKH
0x05E2, //HEBREW LETTER AYIN
0x05E3, //HEBREW LETTER FINAL PE
0x05E4, //HEBREW LETTER PE
0x05E5, //HEBREW LETTER FINAL TSADI
0x05E6, //HEBREW LETTER TSADI
0x05E7, //HEBREW LETTER QOF
0x05E8, //HEBREW LETTER RESH
0x05E9, //HEBREW LETTER SHIN
0x05EA, //HEBREW LETTER TAV
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x200E, //LEFT-TO-RIGHT MARK
0x200F, //RIGHT-TO-LEFT MARK
0xFFFD, //UNDEFINED
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1256 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0x067E, //ARABIC LETTER PEH
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x02C6, //MODIFIER LETTER CIRCUMFLEX ACCENT
0x2030, //PER MILLE SIGN
0x0679, //ARABIC LETTER TTEH
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x0152, //LATIN CAPITAL LIGATURE OE
0x0686, //ARABIC LETTER TCHEH
0x0698, //ARABIC LETTER JEH
0x0688, //ARABIC LETTER DDAL
0x06AF, //ARABIC LETTER GAF
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0x06A9, //ARABIC LETTER KEHEH
0x2122, //TRADE MARK SIGN
0x0691, //ARABIC LETTER RREH
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x0153, //LATIN SMALL LIGATURE OE
0x200C, //ZERO WIDTH NON-JOINER
0x200D, //ZERO WIDTH JOINER
0x06BA, //ARABIC LETTER NOON GHUNNA
0x00A0, //NO-BREAK SPACE
0x060C, //ARABIC COMMA
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x06BE, //ARABIC LETTER HEH DOACHASHMEE
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00AF, //MACRON
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x00B9, //SUPERSCRIPT ONE
0x061B, //ARABIC SEMICOLON
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x061F, //ARABIC QUESTION MARK
0x06C1, //ARABIC LETTER HEH GOAL
0x0621, //ARABIC LETTER HAMZA
0x0622, //ARABIC LETTER ALEF WITH MADDA ABOVE
0x0623, //ARABIC LETTER ALEF WITH HAMZA ABOVE
0x0624, //ARABIC LETTER WAW WITH HAMZA ABOVE
0x0625, //ARABIC LETTER ALEF WITH HAMZA BELOW
0x0626, //ARABIC LETTER YEH WITH HAMZA ABOVE
0x0627, //ARABIC LETTER ALEF
0x0628, //ARABIC LETTER BEH
0x0629, //ARABIC LETTER TEH MARBUTA
0x062A, //ARABIC LETTER TEH
0x062B, //ARABIC LETTER THEH
0x062C, //ARABIC LETTER JEEM
0x062D, //ARABIC LETTER HAH
0x062E, //ARABIC LETTER KHAH
0x062F, //ARABIC LETTER DAL
0x0630, //ARABIC LETTER THAL
0x0631, //ARABIC LETTER REH
0x0632, //ARABIC LETTER ZAIN
0x0633, //ARABIC LETTER SEEN
0x0634, //ARABIC LETTER SHEEN
0x0635, //ARABIC LETTER SAD
0x0636, //ARABIC LETTER DAD
0x00D7, //MULTIPLICATION SIGN
0x0637, //ARABIC LETTER TAH
0x0638, //ARABIC LETTER ZAH
0x0639, //ARABIC LETTER AIN
0x063A, //ARABIC LETTER GHAIN
0x0640, //ARABIC TATWEEL
0x0641, //ARABIC LETTER FEH
0x0642, //ARABIC LETTER QAF
0x0643, //ARABIC LETTER KAF
0x00E0, //LATIN SMALL LETTER A WITH GRAVE
0x0644, //ARABIC LETTER LAM
0x00E2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x0645, //ARABIC LETTER MEEM
0x0646, //ARABIC LETTER NOON
0x0647, //ARABIC LETTER HEH
0x0648, //ARABIC LETTER WAW
0x00E7, //LATIN SMALL LETTER C WITH CEDILLA
0x00E8, //LATIN SMALL LETTER E WITH GRAVE
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x00EA, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB, //LATIN SMALL LETTER E WITH DIAERESIS
0x0649, //ARABIC LETTER ALEF MAKSURA
0x064A, //ARABIC LETTER YEH
0x00EE, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF, //LATIN SMALL LETTER I WITH DIAERESIS
0x064B, //ARABIC FATHATAN
0x064C, //ARABIC DAMMATAN
0x064D, //ARABIC KASRATAN
0x064E, //ARABIC FATHA
0x00F4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x064F, //ARABIC DAMMA
0x0650, //ARABIC KASRA
0x00F7, //DIVISION SIGN
0x0651, //ARABIC SHADDA
0x00F9, //LATIN SMALL LETTER U WITH GRAVE
0x0652, //ARABIC SUKUN
0x00FB, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x200E, //LEFT-TO-RIGHT MARK
0x200F, //RIGHT-TO-LEFT MARK
0x06D2, //ARABIC LETTER YEH BARREE
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1257 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0xFFFD, //UNDEFINED
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0xFFFD, //UNDEFINED
0x2030, //PER MILLE SIGN
0xFFFD, //UNDEFINED
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0x00A8, //DIAERESIS
0x02C7, //CARON
0x00B8, //CEDILLA
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0xFFFD, //UNDEFINED
0x2122, //TRADE MARK SIGN
0xFFFD, //UNDEFINED
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0xFFFD, //UNDEFINED
0x00AF, //MACRON
0x02DB, //OGONEK
0xFFFD, //UNDEFINED
0x00A0, //NO-BREAK SPACE
0xFFFD, //UNDEFINED
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0xFFFD, //UNDEFINED
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00D8, //LATIN CAPITAL LETTER O WITH STROKE
0x00A9, //COPYRIGHT SIGN
0x0156, //LATIN CAPITAL LETTER R WITH CEDILLA
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00C6, //LATIN CAPITAL LETTER AE
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00F8, //LATIN SMALL LETTER O WITH STROKE
0x00B9, //SUPERSCRIPT ONE
0x0157, //LATIN SMALL LETTER R WITH CEDILLA
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x00E6, //LATIN SMALL LETTER AE
0x0104, //LATIN CAPITAL LETTER A WITH OGONEK
0x012E, //LATIN CAPITAL LETTER I WITH OGONEK
0x0100, //LATIN CAPITAL LETTER A WITH MACRON
0x0106, //LATIN CAPITAL LETTER C WITH ACUTE
0x00C4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x0118, //LATIN CAPITAL LETTER E WITH OGONEK
0x0112, //LATIN CAPITAL LETTER E WITH MACRON
0x010C, //LATIN CAPITAL LETTER C WITH CARON
0x00C9, //LATIN CAPITAL LETTER E WITH ACUTE
0x0179, //LATIN CAPITAL LETTER Z WITH ACUTE
0x0116, //LATIN CAPITAL LETTER E WITH DOT ABOVE
0x0122, //LATIN CAPITAL LETTER G WITH CEDILLA
0x0136, //LATIN CAPITAL LETTER K WITH CEDILLA
0x012A, //LATIN CAPITAL LETTER I WITH MACRON
0x013B, //LATIN CAPITAL LETTER L WITH CEDILLA
0x0160, //LATIN CAPITAL LETTER S WITH CARON
0x0143, //LATIN CAPITAL LETTER N WITH ACUTE
0x0145, //LATIN CAPITAL LETTER N WITH CEDILLA
0x00D3, //LATIN CAPITAL LETTER O WITH ACUTE
0x014C, //LATIN CAPITAL LETTER O WITH MACRON
0x00D5, //LATIN CAPITAL LETTER O WITH TILDE
0x00D6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7, //MULTIPLICATION SIGN
0x0172, //LATIN CAPITAL LETTER U WITH OGONEK
0x0141, //LATIN CAPITAL LETTER L WITH STROKE
0x015A, //LATIN CAPITAL LETTER S WITH ACUTE
0x016A, //LATIN CAPITAL LETTER U WITH MACRON
0x00DC, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x017B, //LATIN CAPITAL LETTER Z WITH DOT ABOVE
0x017D, //LATIN CAPITAL LETTER Z WITH CARON
0x00DF, //LATIN SMALL LETTER SHARP S
0x0105, //LATIN SMALL LETTER A WITH OGONEK
0x012F, //LATIN SMALL LETTER I WITH OGONEK
0x0101, //LATIN SMALL LETTER A WITH MACRON
0x0107, //LATIN SMALL LETTER C WITH ACUTE
0x00E4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00E5, //LATIN SMALL LETTER A WITH RING ABOVE
0x0119, //LATIN SMALL LETTER E WITH OGONEK
0x0113, //LATIN SMALL LETTER E WITH MACRON
0x010D, //LATIN SMALL LETTER C WITH CARON
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x017A, //LATIN SMALL LETTER Z WITH ACUTE
0x0117, //LATIN SMALL LETTER E WITH DOT ABOVE
0x0123, //LATIN SMALL LETTER G WITH CEDILLA
0x0137, //LATIN SMALL LETTER K WITH CEDILLA
0x012B, //LATIN SMALL LETTER I WITH MACRON
0x013C, //LATIN SMALL LETTER L WITH CEDILLA
0x0161, //LATIN SMALL LETTER S WITH CARON
0x0144, //LATIN SMALL LETTER N WITH ACUTE
0x0146, //LATIN SMALL LETTER N WITH CEDILLA
0x00F3, //LATIN SMALL LETTER O WITH ACUTE
0x014D, //LATIN SMALL LETTER O WITH MACRON
0x00F5, //LATIN SMALL LETTER O WITH TILDE
0x00F6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00F7, //DIVISION SIGN
0x0173, //LATIN SMALL LETTER U WITH OGONEK
0x0142, //LATIN SMALL LETTER L WITH STROKE
0x015B, //LATIN SMALL LETTER S WITH ACUTE
0x016B, //LATIN SMALL LETTER U WITH MACRON
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x017C, //LATIN SMALL LETTER Z WITH DOT ABOVE
0x017E, //LATIN SMALL LETTER Z WITH CARON
0x02D9, //DOT ABOVE
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp1258 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0x201A, //SINGLE LOW-9 QUOTATION MARK
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x201E, //DOUBLE LOW-9 QUOTATION MARK
0x2026, //HORIZONTAL ELLIPSIS
0x2020, //DAGGER
0x2021, //DOUBLE DAGGER
0x02C6, //MODIFIER LETTER CIRCUMFLEX ACCENT
0x2030, //PER MILLE SIGN
0xFFFD, //UNDEFINED
0x2039, //SINGLE LEFT-POINTING ANGLE QUOTATION MARK
0x0152, //LATIN CAPITAL LIGATURE OE
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0x02DC, //SMALL TILDE
0x2122, //TRADE MARK SIGN
0xFFFD, //UNDEFINED
0x203A, //SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
0x0153, //LATIN SMALL LIGATURE OE
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x0178, //LATIN CAPITAL LETTER Y WITH DIAERESIS
0x00A0, //NO-BREAK SPACE
0x00A1, //INVERTED EXCLAMATION MARK
0x00A2, //CENT SIGN
0x00A3, //POUND SIGN
0x00A4, //CURRENCY SIGN
0x00A5, //YEN SIGN
0x00A6, //BROKEN BAR
0x00A7, //SECTION SIGN
0x00A8, //DIAERESIS
0x00A9, //COPYRIGHT SIGN
0x00AA, //FEMININE ORDINAL INDICATOR
0x00AB, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00AC, //NOT SIGN
0x00AD, //SOFT HYPHEN
0x00AE, //REGISTERED SIGN
0x00AF, //MACRON
0x00B0, //DEGREE SIGN
0x00B1, //PLUS-MINUS SIGN
0x00B2, //SUPERSCRIPT TWO
0x00B3, //SUPERSCRIPT THREE
0x00B4, //ACUTE ACCENT
0x00B5, //MICRO SIGN
0x00B6, //PILCROW SIGN
0x00B7, //MIDDLE DOT
0x00B8, //CEDILLA
0x00B9, //SUPERSCRIPT ONE
0x00BA, //MASCULINE ORDINAL INDICATOR
0x00BB, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00BC, //VULGAR FRACTION ONE QUARTER
0x00BD, //VULGAR FRACTION ONE HALF
0x00BE, //VULGAR FRACTION THREE QUARTERS
0x00BF, //INVERTED QUESTION MARK
0x00C0, //LATIN CAPITAL LETTER A WITH GRAVE
0x00C1, //LATIN CAPITAL LETTER A WITH ACUTE
0x00C2, //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x0102, //LATIN CAPITAL LETTER A WITH BREVE
0x00C4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00C5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x00C6, //LATIN CAPITAL LETTER AE
0x00C7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x00C8, //LATIN CAPITAL LETTER E WITH GRAVE
0x00C9, //LATIN CAPITAL LETTER E WITH ACUTE
0x00CA, //LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00CB, //LATIN CAPITAL LETTER E WITH DIAERESIS
0x0300, //COMBINING GRAVE ACCENT
0x00CD, //LATIN CAPITAL LETTER I WITH ACUTE
0x00CE, //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00CF, //LATIN CAPITAL LETTER I WITH DIAERESIS
0x0110, //LATIN CAPITAL LETTER D WITH STROKE
0x00D1, //LATIN CAPITAL LETTER N WITH TILDE
0x0309, //COMBINING HOOK ABOVE
0x00D3, //LATIN CAPITAL LETTER O WITH ACUTE
0x00D4, //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x01A0, //LATIN CAPITAL LETTER O WITH HORN
0x00D6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00D7, //MULTIPLICATION SIGN
0x00D8, //LATIN CAPITAL LETTER O WITH STROKE
0x00D9, //LATIN CAPITAL LETTER U WITH GRAVE
0x00DA, //LATIN CAPITAL LETTER U WITH ACUTE
0x00DB, //LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00DC, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x01AF, //LATIN CAPITAL LETTER U WITH HORN
0x0303, //COMBINING TILDE
0x00DF, //LATIN SMALL LETTER SHARP S
0x00E0, //LATIN SMALL LETTER A WITH GRAVE
0x00E1, //LATIN SMALL LETTER A WITH ACUTE
0x00E2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x0103, //LATIN SMALL LETTER A WITH BREVE
0x00E4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00E5, //LATIN SMALL LETTER A WITH RING ABOVE
0x00E6, //LATIN SMALL LETTER AE
0x00E7, //LATIN SMALL LETTER C WITH CEDILLA
0x00E8, //LATIN SMALL LETTER E WITH GRAVE
0x00E9, //LATIN SMALL LETTER E WITH ACUTE
0x00EA, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00EB, //LATIN SMALL LETTER E WITH DIAERESIS
0x0301, //COMBINING ACUTE ACCENT
0x00ED, //LATIN SMALL LETTER I WITH ACUTE
0x00EE, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00EF, //LATIN SMALL LETTER I WITH DIAERESIS
0x0111, //LATIN SMALL LETTER D WITH STROKE
0x00F1, //LATIN SMALL LETTER N WITH TILDE
0x0323, //COMBINING DOT BELOW
0x00F3, //LATIN SMALL LETTER O WITH ACUTE
0x00F4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x01A1, //LATIN SMALL LETTER O WITH HORN
0x00F6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00F7, //DIVISION SIGN
0x00F8, //LATIN SMALL LETTER O WITH STROKE
0x00F9, //LATIN SMALL LETTER U WITH GRAVE
0x00FA, //LATIN SMALL LETTER U WITH ACUTE
0x00FB, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00FC, //LATIN SMALL LETTER U WITH DIAERESIS
0x01B0, //LATIN SMALL LETTER U WITH HORN
0x20AB, //DONG SIGN
0x00FF, //LATIN SMALL LETTER Y WITH DIAERESIS
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp437 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000a, //LINE FEED
0x000b, //VERTICAL TABULATION
0x000c, //FORM FEED
0x000d, //CARRIAGE RETURN
0x000e, //SHIFT OUT
0x000f, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001a, //SUBSTITUTE
0x001b, //ESCAPE
0x001c, //FILE SEPARATOR
0x001d, //GROUP SEPARATOR
0x001e, //RECORD SEPARATOR
0x001f, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002a, //ASTERISK
0x002b, //PLUS SIGN
0x002c, //COMMA
0x002d, //HYPHEN-MINUS
0x002e, //FULL STOP
0x002f, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003a, //COLON
0x003b, //SEMICOLON
0x003c, //LESS-THAN SIGN
0x003d, //EQUALS SIGN
0x003e, //GREATER-THAN SIGN
0x003f, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004a, //LATIN CAPITAL LETTER J
0x004b, //LATIN CAPITAL LETTER K
0x004c, //LATIN CAPITAL LETTER L
0x004d, //LATIN CAPITAL LETTER M
0x004e, //LATIN CAPITAL LETTER N
0x004f, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005a, //LATIN CAPITAL LETTER Z
0x005b, //LEFT SQUARE BRACKET
0x005c, //REVERSE SOLIDUS
0x005d, //RIGHT SQUARE BRACKET
0x005e, //CIRCUMFLEX ACCENT
0x005f, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006a, //LATIN SMALL LETTER J
0x006b, //LATIN SMALL LETTER K
0x006c, //LATIN SMALL LETTER L
0x006d, //LATIN SMALL LETTER M
0x006e, //LATIN SMALL LETTER N
0x006f, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007a, //LATIN SMALL LETTER Z
0x007b, //LEFT CURLY BRACKET
0x007c, //VERTICAL LINE
0x007d, //RIGHT CURLY BRACKET
0x007e, //TILDE
0x007f, //DELETE
0x00c7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x00fc, //LATIN SMALL LETTER U WITH DIAERESIS
0x00e9, //LATIN SMALL LETTER E WITH ACUTE
0x00e2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00e4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00e0, //LATIN SMALL LETTER A WITH GRAVE
0x00e5, //LATIN SMALL LETTER A WITH RING ABOVE
0x00e7, //LATIN SMALL LETTER C WITH CEDILLA
0x00ea, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00eb, //LATIN SMALL LETTER E WITH DIAERESIS
0x00e8, //LATIN SMALL LETTER E WITH GRAVE
0x00ef, //LATIN SMALL LETTER I WITH DIAERESIS
0x00ee, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00ec, //LATIN SMALL LETTER I WITH GRAVE
0x00c4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00c5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x00c9, //LATIN CAPITAL LETTER E WITH ACUTE
0x00e6, //LATIN SMALL LIGATURE AE
0x00c6, //LATIN CAPITAL LIGATURE AE
0x00f4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00f6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00f2, //LATIN SMALL LETTER O WITH GRAVE
0x00fb, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00f9, //LATIN SMALL LETTER U WITH GRAVE
0x00ff, //LATIN SMALL LETTER Y WITH DIAERESIS
0x00d6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00dc, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x00a2, //CENT SIGN
0x00a3, //POUND SIGN
0x00a5, //YEN SIGN
0x20a7, //PESETA SIGN
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x00e1, //LATIN SMALL LETTER A WITH ACUTE
0x00ed, //LATIN SMALL LETTER I WITH ACUTE
0x00f3, //LATIN SMALL LETTER O WITH ACUTE
0x00fa, //LATIN SMALL LETTER U WITH ACUTE
0x00f1, //LATIN SMALL LETTER N WITH TILDE
0x00d1, //LATIN CAPITAL LETTER N WITH TILDE
0x00aa, //FEMININE ORDINAL INDICATOR
0x00ba, //MASCULINE ORDINAL INDICATOR
0x00bf, //INVERTED QUESTION MARK
0x2310, //REVERSED NOT SIGN
0x00ac, //NOT SIGN
0x00bd, //VULGAR FRACTION ONE HALF
0x00bc, //VULGAR FRACTION ONE QUARTER
0x00a1, //INVERTED EXCLAMATION MARK
0x00ab, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00bb, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x2591, //LIGHT SHADE
0x2592, //MEDIUM SHADE
0x2593, //DARK SHADE
0x2502, //BOX DRAWINGS LIGHT VERTICAL
0x2524, //BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x2561, //BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
0x2562, //BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
0x2556, //BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
0x2555, //BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
0x2563, //BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0x2551, //BOX DRAWINGS DOUBLE VERTICAL
0x2557, //BOX DRAWINGS DOUBLE DOWN AND LEFT
0x255d, //BOX DRAWINGS DOUBLE UP AND LEFT
0x255c, //BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
0x255b, //BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
0x2510, //BOX DRAWINGS LIGHT DOWN AND LEFT
0x2514, //BOX DRAWINGS LIGHT UP AND RIGHT
0x2534, //BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x252c, //BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x251c, //BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x2500, //BOX DRAWINGS LIGHT HORIZONTAL
0x253c, //BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x255e, //BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
0x255f, //BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
0x255a, //BOX DRAWINGS DOUBLE UP AND RIGHT
0x2554, //BOX DRAWINGS DOUBLE DOWN AND RIGHT
0x2569, //BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0x2566, //BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0x2560, //BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0x2550, //BOX DRAWINGS DOUBLE HORIZONTAL
0x256c, //BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0x2567, //BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
0x2568, //BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
0x2564, //BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
0x2565, //BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
0x2559, //BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
0x2558, //BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
0x2552, //BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
0x2553, //BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
0x256b, //BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
0x256a, //BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
0x2518, //BOX DRAWINGS LIGHT UP AND LEFT
0x250c, //BOX DRAWINGS LIGHT DOWN AND RIGHT
0x2588, //FULL BLOCK
0x2584, //LOWER HALF BLOCK
0x258c, //LEFT HALF BLOCK
0x2590, //RIGHT HALF BLOCK
0x2580, //UPPER HALF BLOCK
0x03b1, //GREEK SMALL LETTER ALPHA
0x00df, //LATIN SMALL LETTER SHARP S
0x0393, //GREEK CAPITAL LETTER GAMMA
0x03c0, //GREEK SMALL LETTER PI
0x03a3, //GREEK CAPITAL LETTER SIGMA
0x03c3, //GREEK SMALL LETTER SIGMA
0x00b5, //MICRO SIGN
0x03c4, //GREEK SMALL LETTER TAU
0x03a6, //GREEK CAPITAL LETTER PHI
0x0398, //GREEK CAPITAL LETTER THETA
0x03a9, //GREEK CAPITAL LETTER OMEGA
0x03b4, //GREEK SMALL LETTER DELTA
0x221e, //INFINITY
0x03c6, //GREEK SMALL LETTER PHI
0x03b5, //GREEK SMALL LETTER EPSILON
0x2229, //INTERSECTION
0x2261, //IDENTICAL TO
0x00b1, //PLUS-MINUS SIGN
0x2265, //GREATER-THAN OR EQUAL TO
0x2264, //LESS-THAN OR EQUAL TO
0x2320, //TOP HALF INTEGRAL
0x2321, //BOTTOM HALF INTEGRAL
0x00f7, //DIVISION SIGN
0x2248, //ALMOST EQUAL TO
0x00b0, //DEGREE SIGN
0x2219, //BULLET OPERATOR
0x00b7, //MIDDLE DOT
0x221a, //SQUARE ROOT
0x207f, //SUPERSCRIPT LATIN SMALL LETTER N
0x00b2, //SUPERSCRIPT TWO
0x25a0, //BLACK SQUARE
0x00a0, //NO-BREAK SPACE
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp850 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000a, //LINE FEED
0x000b, //VERTICAL TABULATION
0x000c, //FORM FEED
0x000d, //CARRIAGE RETURN
0x000e, //SHIFT OUT
0x000f, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001a, //SUBSTITUTE
0x001b, //ESCAPE
0x001c, //FILE SEPARATOR
0x001d, //GROUP SEPARATOR
0x001e, //RECORD SEPARATOR
0x001f, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002a, //ASTERISK
0x002b, //PLUS SIGN
0x002c, //COMMA
0x002d, //HYPHEN-MINUS
0x002e, //FULL STOP
0x002f, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003a, //COLON
0x003b, //SEMICOLON
0x003c, //LESS-THAN SIGN
0x003d, //EQUALS SIGN
0x003e, //GREATER-THAN SIGN
0x003f, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004a, //LATIN CAPITAL LETTER J
0x004b, //LATIN CAPITAL LETTER K
0x004c, //LATIN CAPITAL LETTER L
0x004d, //LATIN CAPITAL LETTER M
0x004e, //LATIN CAPITAL LETTER N
0x004f, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005a, //LATIN CAPITAL LETTER Z
0x005b, //LEFT SQUARE BRACKET
0x005c, //REVERSE SOLIDUS
0x005d, //RIGHT SQUARE BRACKET
0x005e, //CIRCUMFLEX ACCENT
0x005f, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006a, //LATIN SMALL LETTER J
0x006b, //LATIN SMALL LETTER K
0x006c, //LATIN SMALL LETTER L
0x006d, //LATIN SMALL LETTER M
0x006e, //LATIN SMALL LETTER N
0x006f, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007a, //LATIN SMALL LETTER Z
0x007b, //LEFT CURLY BRACKET
0x007c, //VERTICAL LINE
0x007d, //RIGHT CURLY BRACKET
0x007e, //TILDE
0x007f, //DELETE
0x00c7, //LATIN CAPITAL LETTER C WITH CEDILLA
0x00fc, //LATIN SMALL LETTER U WITH DIAERESIS
0x00e9, //LATIN SMALL LETTER E WITH ACUTE
0x00e2, //LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00e4, //LATIN SMALL LETTER A WITH DIAERESIS
0x00e0, //LATIN SMALL LETTER A WITH GRAVE
0x00e5, //LATIN SMALL LETTER A WITH RING ABOVE
0x00e7, //LATIN SMALL LETTER C WITH CEDILLA
0x00ea, //LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00eb, //LATIN SMALL LETTER E WITH DIAERESIS
0x00e8, //LATIN SMALL LETTER E WITH GRAVE
0x00ef, //LATIN SMALL LETTER I WITH DIAERESIS
0x00ee, //LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00ec, //LATIN SMALL LETTER I WITH GRAVE
0x00c4, //LATIN CAPITAL LETTER A WITH DIAERESIS
0x00c5, //LATIN CAPITAL LETTER A WITH RING ABOVE
0x00c9, //LATIN CAPITAL LETTER E WITH ACUTE
0x00e6, //LATIN SMALL LIGATURE AE
0x00c6, //LATIN CAPITAL LIGATURE AE
0x00f4, //LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00f6, //LATIN SMALL LETTER O WITH DIAERESIS
0x00f2, //LATIN SMALL LETTER O WITH GRAVE
0x00fb, //LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00f9, //LATIN SMALL LETTER U WITH GRAVE
0x00ff, //LATIN SMALL LETTER Y WITH DIAERESIS
0x00d6, //LATIN CAPITAL LETTER O WITH DIAERESIS
0x00dc, //LATIN CAPITAL LETTER U WITH DIAERESIS
0x00f8, //LATIN SMALL LETTER O WITH STROKE
0x00a3, //POUND SIGN
0x00d8, //LATIN CAPITAL LETTER O WITH STROKE
0x00d7, //MULTIPLICATION SIGN
0x0192, //LATIN SMALL LETTER F WITH HOOK
0x00e1, //LATIN SMALL LETTER A WITH ACUTE
0x00ed, //LATIN SMALL LETTER I WITH ACUTE
0x00f3, //LATIN SMALL LETTER O WITH ACUTE
0x00fa, //LATIN SMALL LETTER U WITH ACUTE
0x00f1, //LATIN SMALL LETTER N WITH TILDE
0x00d1, //LATIN CAPITAL LETTER N WITH TILDE
0x00aa, //FEMININE ORDINAL INDICATOR
0x00ba, //MASCULINE ORDINAL INDICATOR
0x00bf, //INVERTED QUESTION MARK
0x00ae, //REGISTERED SIGN
0x00ac, //NOT SIGN
0x00bd, //VULGAR FRACTION ONE HALF
0x00bc, //VULGAR FRACTION ONE QUARTER
0x00a1, //INVERTED EXCLAMATION MARK
0x00ab, //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00bb, //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x2591, //LIGHT SHADE
0x2592, //MEDIUM SHADE
0x2593, //DARK SHADE
0x2502, //BOX DRAWINGS LIGHT VERTICAL
0x2524, //BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x00c1, //LATIN CAPITAL LETTER A WITH ACUTE
0x00c2, //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
0x00c0, //LATIN CAPITAL LETTER A WITH GRAVE
0x00a9, //COPYRIGHT SIGN
0x2563, //BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0x2551, //BOX DRAWINGS DOUBLE VERTICAL
0x2557, //BOX DRAWINGS DOUBLE DOWN AND LEFT
0x255d, //BOX DRAWINGS DOUBLE UP AND LEFT
0x00a2, //CENT SIGN
0x00a5, //YEN SIGN
0x2510, //BOX DRAWINGS LIGHT DOWN AND LEFT
0x2514, //BOX DRAWINGS LIGHT UP AND RIGHT
0x2534, //BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x252c, //BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x251c, //BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x2500, //BOX DRAWINGS LIGHT HORIZONTAL
0x253c, //BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x00e3, //LATIN SMALL LETTER A WITH TILDE
0x00c3, //LATIN CAPITAL LETTER A WITH TILDE
0x255a, //BOX DRAWINGS DOUBLE UP AND RIGHT
0x2554, //BOX DRAWINGS DOUBLE DOWN AND RIGHT
0x2569, //BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0x2566, //BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0x2560, //BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0x2550, //BOX DRAWINGS DOUBLE HORIZONTAL
0x256c, //BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0x00a4, //CURRENCY SIGN
0x00f0, //LATIN SMALL LETTER ETH
0x00d0, //LATIN CAPITAL LETTER ETH
0x00ca, //LATIN CAPITAL LETTER E WITH CIRCUMFLEX
0x00cb, //LATIN CAPITAL LETTER E WITH DIAERESIS
0x00c8, //LATIN CAPITAL LETTER E WITH GRAVE
0x0131, //LATIN SMALL LETTER DOTLESS I
0x00cd, //LATIN CAPITAL LETTER I WITH ACUTE
0x00ce, //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
0x00cf, //LATIN CAPITAL LETTER I WITH DIAERESIS
0x2518, //BOX DRAWINGS LIGHT UP AND LEFT
0x250c, //BOX DRAWINGS LIGHT DOWN AND RIGHT
0x2588, //FULL BLOCK
0x2584, //LOWER HALF BLOCK
0x00a6, //BROKEN BAR
0x00cc, //LATIN CAPITAL LETTER I WITH GRAVE
0x2580, //UPPER HALF BLOCK
0x00d3, //LATIN CAPITAL LETTER O WITH ACUTE
0x00df, //LATIN SMALL LETTER SHARP S
0x00d4, //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
0x00d2, //LATIN CAPITAL LETTER O WITH GRAVE
0x00f5, //LATIN SMALL LETTER O WITH TILDE
0x00d5, //LATIN CAPITAL LETTER O WITH TILDE
0x00b5, //MICRO SIGN
0x00fe, //LATIN SMALL LETTER THORN
0x00de, //LATIN CAPITAL LETTER THORN
0x00da, //LATIN CAPITAL LETTER U WITH ACUTE
0x00db, //LATIN CAPITAL LETTER U WITH CIRCUMFLEX
0x00d9, //LATIN CAPITAL LETTER U WITH GRAVE
0x00fd, //LATIN SMALL LETTER Y WITH ACUTE
0x00dd, //LATIN CAPITAL LETTER Y WITH ACUTE
0x00af, //MACRON
0x00b4, //ACUTE ACCENT
0x00ad, //SOFT HYPHEN
0x00b1, //PLUS-MINUS SIGN
0x2017, //DOUBLE LOW LINE
0x00be, //VULGAR FRACTION THREE QUARTERS
0x00b6, //PILCROW SIGN
0x00a7, //SECTION SIGN
0x00f7, //DIVISION SIGN
0x00b8, //CEDILLA
0x00b0, //DEGREE SIGN
0x00a8, //DIAERESIS
0x00b7, //MIDDLE DOT
0x00b9, //SUPERSCRIPT ONE
0x00b3, //SUPERSCRIPT THREE
0x00b2, //SUPERSCRIPT TWO
0x25a0, //BLACK SQUARE
0x00a0, //NO-BREAK SPACE
},
}
-262
View File
@@ -1,262 +0,0 @@
package cp
var cp874 *charsetMap = &charsetMap{
sb: [256]rune{
0x0000, //NULL
0x0001, //START OF HEADING
0x0002, //START OF TEXT
0x0003, //END OF TEXT
0x0004, //END OF TRANSMISSION
0x0005, //ENQUIRY
0x0006, //ACKNOWLEDGE
0x0007, //BELL
0x0008, //BACKSPACE
0x0009, //HORIZONTAL TABULATION
0x000A, //LINE FEED
0x000B, //VERTICAL TABULATION
0x000C, //FORM FEED
0x000D, //CARRIAGE RETURN
0x000E, //SHIFT OUT
0x000F, //SHIFT IN
0x0010, //DATA LINK ESCAPE
0x0011, //DEVICE CONTROL ONE
0x0012, //DEVICE CONTROL TWO
0x0013, //DEVICE CONTROL THREE
0x0014, //DEVICE CONTROL FOUR
0x0015, //NEGATIVE ACKNOWLEDGE
0x0016, //SYNCHRONOUS IDLE
0x0017, //END OF TRANSMISSION BLOCK
0x0018, //CANCEL
0x0019, //END OF MEDIUM
0x001A, //SUBSTITUTE
0x001B, //ESCAPE
0x001C, //FILE SEPARATOR
0x001D, //GROUP SEPARATOR
0x001E, //RECORD SEPARATOR
0x001F, //UNIT SEPARATOR
0x0020, //SPACE
0x0021, //EXCLAMATION MARK
0x0022, //QUOTATION MARK
0x0023, //NUMBER SIGN
0x0024, //DOLLAR SIGN
0x0025, //PERCENT SIGN
0x0026, //AMPERSAND
0x0027, //APOSTROPHE
0x0028, //LEFT PARENTHESIS
0x0029, //RIGHT PARENTHESIS
0x002A, //ASTERISK
0x002B, //PLUS SIGN
0x002C, //COMMA
0x002D, //HYPHEN-MINUS
0x002E, //FULL STOP
0x002F, //SOLIDUS
0x0030, //DIGIT ZERO
0x0031, //DIGIT ONE
0x0032, //DIGIT TWO
0x0033, //DIGIT THREE
0x0034, //DIGIT FOUR
0x0035, //DIGIT FIVE
0x0036, //DIGIT SIX
0x0037, //DIGIT SEVEN
0x0038, //DIGIT EIGHT
0x0039, //DIGIT NINE
0x003A, //COLON
0x003B, //SEMICOLON
0x003C, //LESS-THAN SIGN
0x003D, //EQUALS SIGN
0x003E, //GREATER-THAN SIGN
0x003F, //QUESTION MARK
0x0040, //COMMERCIAL AT
0x0041, //LATIN CAPITAL LETTER A
0x0042, //LATIN CAPITAL LETTER B
0x0043, //LATIN CAPITAL LETTER C
0x0044, //LATIN CAPITAL LETTER D
0x0045, //LATIN CAPITAL LETTER E
0x0046, //LATIN CAPITAL LETTER F
0x0047, //LATIN CAPITAL LETTER G
0x0048, //LATIN CAPITAL LETTER H
0x0049, //LATIN CAPITAL LETTER I
0x004A, //LATIN CAPITAL LETTER J
0x004B, //LATIN CAPITAL LETTER K
0x004C, //LATIN CAPITAL LETTER L
0x004D, //LATIN CAPITAL LETTER M
0x004E, //LATIN CAPITAL LETTER N
0x004F, //LATIN CAPITAL LETTER O
0x0050, //LATIN CAPITAL LETTER P
0x0051, //LATIN CAPITAL LETTER Q
0x0052, //LATIN CAPITAL LETTER R
0x0053, //LATIN CAPITAL LETTER S
0x0054, //LATIN CAPITAL LETTER T
0x0055, //LATIN CAPITAL LETTER U
0x0056, //LATIN CAPITAL LETTER V
0x0057, //LATIN CAPITAL LETTER W
0x0058, //LATIN CAPITAL LETTER X
0x0059, //LATIN CAPITAL LETTER Y
0x005A, //LATIN CAPITAL LETTER Z
0x005B, //LEFT SQUARE BRACKET
0x005C, //REVERSE SOLIDUS
0x005D, //RIGHT SQUARE BRACKET
0x005E, //CIRCUMFLEX ACCENT
0x005F, //LOW LINE
0x0060, //GRAVE ACCENT
0x0061, //LATIN SMALL LETTER A
0x0062, //LATIN SMALL LETTER B
0x0063, //LATIN SMALL LETTER C
0x0064, //LATIN SMALL LETTER D
0x0065, //LATIN SMALL LETTER E
0x0066, //LATIN SMALL LETTER F
0x0067, //LATIN SMALL LETTER G
0x0068, //LATIN SMALL LETTER H
0x0069, //LATIN SMALL LETTER I
0x006A, //LATIN SMALL LETTER J
0x006B, //LATIN SMALL LETTER K
0x006C, //LATIN SMALL LETTER L
0x006D, //LATIN SMALL LETTER M
0x006E, //LATIN SMALL LETTER N
0x006F, //LATIN SMALL LETTER O
0x0070, //LATIN SMALL LETTER P
0x0071, //LATIN SMALL LETTER Q
0x0072, //LATIN SMALL LETTER R
0x0073, //LATIN SMALL LETTER S
0x0074, //LATIN SMALL LETTER T
0x0075, //LATIN SMALL LETTER U
0x0076, //LATIN SMALL LETTER V
0x0077, //LATIN SMALL LETTER W
0x0078, //LATIN SMALL LETTER X
0x0079, //LATIN SMALL LETTER Y
0x007A, //LATIN SMALL LETTER Z
0x007B, //LEFT CURLY BRACKET
0x007C, //VERTICAL LINE
0x007D, //RIGHT CURLY BRACKET
0x007E, //TILDE
0x007F, //DELETE
0x20AC, //EURO SIGN
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2026, //HORIZONTAL ELLIPSIS
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x2018, //LEFT SINGLE QUOTATION MARK
0x2019, //RIGHT SINGLE QUOTATION MARK
0x201C, //LEFT DOUBLE QUOTATION MARK
0x201D, //RIGHT DOUBLE QUOTATION MARK
0x2022, //BULLET
0x2013, //EN DASH
0x2014, //EM DASH
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x00A0, //NO-BREAK SPACE
0x0E01, //THAI CHARACTER KO KAI
0x0E02, //THAI CHARACTER KHO KHAI
0x0E03, //THAI CHARACTER KHO KHUAT
0x0E04, //THAI CHARACTER KHO KHWAI
0x0E05, //THAI CHARACTER KHO KHON
0x0E06, //THAI CHARACTER KHO RAKHANG
0x0E07, //THAI CHARACTER NGO NGU
0x0E08, //THAI CHARACTER CHO CHAN
0x0E09, //THAI CHARACTER CHO CHING
0x0E0A, //THAI CHARACTER CHO CHANG
0x0E0B, //THAI CHARACTER SO SO
0x0E0C, //THAI CHARACTER CHO CHOE
0x0E0D, //THAI CHARACTER YO YING
0x0E0E, //THAI CHARACTER DO CHADA
0x0E0F, //THAI CHARACTER TO PATAK
0x0E10, //THAI CHARACTER THO THAN
0x0E11, //THAI CHARACTER THO NANGMONTHO
0x0E12, //THAI CHARACTER THO PHUTHAO
0x0E13, //THAI CHARACTER NO NEN
0x0E14, //THAI CHARACTER DO DEK
0x0E15, //THAI CHARACTER TO TAO
0x0E16, //THAI CHARACTER THO THUNG
0x0E17, //THAI CHARACTER THO THAHAN
0x0E18, //THAI CHARACTER THO THONG
0x0E19, //THAI CHARACTER NO NU
0x0E1A, //THAI CHARACTER BO BAIMAI
0x0E1B, //THAI CHARACTER PO PLA
0x0E1C, //THAI CHARACTER PHO PHUNG
0x0E1D, //THAI CHARACTER FO FA
0x0E1E, //THAI CHARACTER PHO PHAN
0x0E1F, //THAI CHARACTER FO FAN
0x0E20, //THAI CHARACTER PHO SAMPHAO
0x0E21, //THAI CHARACTER MO MA
0x0E22, //THAI CHARACTER YO YAK
0x0E23, //THAI CHARACTER RO RUA
0x0E24, //THAI CHARACTER RU
0x0E25, //THAI CHARACTER LO LING
0x0E26, //THAI CHARACTER LU
0x0E27, //THAI CHARACTER WO WAEN
0x0E28, //THAI CHARACTER SO SALA
0x0E29, //THAI CHARACTER SO RUSI
0x0E2A, //THAI CHARACTER SO SUA
0x0E2B, //THAI CHARACTER HO HIP
0x0E2C, //THAI CHARACTER LO CHULA
0x0E2D, //THAI CHARACTER O ANG
0x0E2E, //THAI CHARACTER HO NOKHUK
0x0E2F, //THAI CHARACTER PAIYANNOI
0x0E30, //THAI CHARACTER SARA A
0x0E31, //THAI CHARACTER MAI HAN-AKAT
0x0E32, //THAI CHARACTER SARA AA
0x0E33, //THAI CHARACTER SARA AM
0x0E34, //THAI CHARACTER SARA I
0x0E35, //THAI CHARACTER SARA II
0x0E36, //THAI CHARACTER SARA UE
0x0E37, //THAI CHARACTER SARA UEE
0x0E38, //THAI CHARACTER SARA U
0x0E39, //THAI CHARACTER SARA UU
0x0E3A, //THAI CHARACTER PHINTHU
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0x0E3F, //THAI CURRENCY SYMBOL BAHT
0x0E40, //THAI CHARACTER SARA E
0x0E41, //THAI CHARACTER SARA AE
0x0E42, //THAI CHARACTER SARA O
0x0E43, //THAI CHARACTER SARA AI MAIMUAN
0x0E44, //THAI CHARACTER SARA AI MAIMALAI
0x0E45, //THAI CHARACTER LAKKHANGYAO
0x0E46, //THAI CHARACTER MAIYAMOK
0x0E47, //THAI CHARACTER MAITAIKHU
0x0E48, //THAI CHARACTER MAI EK
0x0E49, //THAI CHARACTER MAI THO
0x0E4A, //THAI CHARACTER MAI TRI
0x0E4B, //THAI CHARACTER MAI CHATTAWA
0x0E4C, //THAI CHARACTER THANTHAKHAT
0x0E4D, //THAI CHARACTER NIKHAHIT
0x0E4E, //THAI CHARACTER YAMAKKAN
0x0E4F, //THAI CHARACTER FONGMAN
0x0E50, //THAI DIGIT ZERO
0x0E51, //THAI DIGIT ONE
0x0E52, //THAI DIGIT TWO
0x0E53, //THAI DIGIT THREE
0x0E54, //THAI DIGIT FOUR
0x0E55, //THAI DIGIT FIVE
0x0E56, //THAI DIGIT SIX
0x0E57, //THAI DIGIT SEVEN
0x0E58, //THAI DIGIT EIGHT
0x0E59, //THAI DIGIT NINE
0x0E5A, //THAI CHARACTER ANGKHANKHU
0x0E5B, //THAI CHARACTER KHOMUT
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
0xFFFD, //UNDEFINED
},
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-252
View File
@@ -1,252 +0,0 @@
package decimal
import (
"encoding/binary"
"errors"
"fmt"
"math"
"math/big"
"strings"
)
// Decimal represents decimal type in the Microsoft Open Specifications: http://msdn.microsoft.com/en-us/library/ee780893.aspx
type Decimal struct {
integer [4]uint32 // Little-endian
positive bool
prec uint8
scale uint8
}
var (
scaletblflt64 [39]float64
int10 big.Int
int1e5 big.Int
)
func init() {
var acc float64 = 1
for i := 0; i <= 38; i++ {
scaletblflt64[i] = acc
acc *= 10
}
int10.SetInt64(10)
int1e5.SetInt64(1e5)
}
const autoScale = 100
// SetInteger sets the ind'th element in the integer array
func (d *Decimal) SetInteger(integer uint32, ind uint8) {
d.integer[ind] = integer
}
// SetPositive sets the positive member
func (d *Decimal) SetPositive(positive bool) {
d.positive = positive
}
// SetPrec sets the prec member
func (d *Decimal) SetPrec(prec uint8) {
d.prec = prec
}
// SetScale sets the scale member
func (d *Decimal) SetScale(scale uint8) {
d.scale = scale
}
// IsPositive returns true if the Decimal is positive
func (d *Decimal) IsPositive() bool {
return d.positive
}
// ToFloat64 converts decimal to a float64
func (d Decimal) ToFloat64() float64 {
val := float64(0)
for i := 3; i >= 0; i-- {
val *= 0x100000000
val += float64(d.integer[i])
}
if !d.positive {
val = -val
}
if d.scale != 0 {
val /= scaletblflt64[d.scale]
}
return val
}
// BigInt converts decimal to a bigint
func (d Decimal) BigInt() big.Int {
bytes := make([]byte, 16)
binary.BigEndian.PutUint32(bytes[0:4], d.integer[3])
binary.BigEndian.PutUint32(bytes[4:8], d.integer[2])
binary.BigEndian.PutUint32(bytes[8:12], d.integer[1])
binary.BigEndian.PutUint32(bytes[12:16], d.integer[0])
var x big.Int
x.SetBytes(bytes)
if !d.positive {
x.Neg(&x)
}
return x
}
// Bytes converts decimal to a scaled byte slice
func (d Decimal) Bytes() []byte {
x := d.BigInt()
return ScaleBytes(x.String(), d.scale)
}
// UnscaledBytes converts decimal to a unscaled byte slice
func (d Decimal) UnscaledBytes() []byte {
x := d.BigInt()
return x.Bytes()
}
// String converts decimal to a string
func (d Decimal) String() string {
return string(d.Bytes())
}
// Float64ToDecimal converts float64 to decimal
func Float64ToDecimal(f float64) (Decimal, error) {
return Float64ToDecimalScale(f, autoScale)
}
// Float64ToDecimalScale converts float64 to decimal; user can specify the scale
func Float64ToDecimalScale(f float64, scale uint8) (Decimal, error) {
var dec Decimal
if math.IsNaN(f) {
return dec, errors.New("NaN")
}
if math.IsInf(f, 0) {
return dec, errors.New("Infinity can't be converted to decimal")
}
dec.positive = f >= 0
if !dec.positive {
f = math.Abs(f)
}
if f > 3.402823669209385e+38 {
return dec, errors.New("Float value is out of range")
}
dec.prec = 20
var integer float64
for dec.scale = 0; dec.scale <= scale; dec.scale++ {
integer = f * scaletblflt64[dec.scale]
_, frac := math.Modf(integer)
if frac == 0 && scale == autoScale {
break
}
}
for i := 0; i < 4; i++ {
mod := math.Mod(integer, 0x100000000)
integer -= mod
integer /= 0x100000000
dec.integer[i] = uint32(mod)
if mod-math.Trunc(mod) >= 0.5 {
dec.integer[i] = uint32(mod) + 1
}
}
return dec, nil
}
// Int64ToDecimalScale converts float64 to decimal; user can specify the scale
func Int64ToDecimalScale(v int64, scale uint8) Decimal {
positive := v >= 0
if !positive {
if v == math.MinInt64 {
// Special case - can't negate
return Decimal{
integer: [4]uint32{0, 0x80000000, 0, 0},
positive: false,
prec: 20,
scale: 0,
}
}
v = -v
}
return Decimal{
integer: [4]uint32{uint32(v), uint32(v >> 32), 0, 0},
positive: positive,
prec: 20,
scale: scale,
}
}
// StringToDecimalScale converts string to decimal
func StringToDecimalScale(v string, outScale uint8) (Decimal, error) {
var r big.Int
var unscaled string
var inScale int
point := strings.LastIndexByte(v, '.')
if point == -1 {
inScale = 0
unscaled = v
} else {
inScale = len(v) - point - 1
unscaled = v[:point] + v[point+1:]
}
if inScale > math.MaxUint8 {
return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale too large", v)
}
_, ok := r.SetString(unscaled, 10)
if !ok {
return Decimal{}, fmt.Errorf("can't parse %q as a decimal number", v)
}
if inScale > int(outScale) {
return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale %d is larger than the scale %d of the target column", v, inScale, outScale)
}
for inScale < int(outScale) {
if int(outScale)-inScale >= 5 {
r.Mul(&r, &int1e5)
inScale += 5
} else {
r.Mul(&r, &int10)
inScale++
}
}
bytes := r.Bytes()
if len(bytes) > 16 {
return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: precision too large", v)
}
var out [4]uint32
for i, b := range bytes {
pos := len(bytes) - i - 1
out[pos/4] += uint32(b) << uint(pos%4*8)
}
return Decimal{
integer: out,
positive: r.Sign() >= 0,
prec: 20,
scale: uint8(inScale),
}, nil
}
// ScaleBytes converts a stringified decimal to a scaled byte slice
func ScaleBytes(s string, scale uint8) []byte {
z := make([]byte, 0, len(s)+1)
if s[0] == '-' || s[0] == '+' {
z = append(z, byte(s[0]))
s = s[1:]
}
pos := len(s) - int(scale)
if pos <= 0 {
z = append(z, byte('0'))
} else if pos > 0 {
z = append(z, []byte(s[:pos])...)
}
if scale > 0 {
z = append(z, byte('.'))
for pos < 0 {
z = append(z, byte('0'))
pos++
}
z = append(z, []byte(s[pos:])...)
}
return z
}
-265
View File
@@ -1,265 +0,0 @@
// Package querytext is the old query parser and parameter substitute process.
// Do not use on new code.
//
// This package is not subject to any API compatibility guarantee.
package querytext
import (
"bytes"
"io"
"strconv"
)
type parser struct {
r *bytes.Reader
w bytes.Buffer
paramCount int
paramMax int
// using map as a set
namedParams map[string]bool
}
func (p *parser) next() (rune, bool) {
ch, _, err := p.r.ReadRune()
if err != nil {
if err != io.EOF {
panic(err)
}
return 0, false
}
return ch, true
}
func (p *parser) unread() {
err := p.r.UnreadRune()
if err != nil {
panic(err)
}
}
func (p *parser) write(ch rune) {
p.w.WriteRune(ch)
}
type stateFunc func(*parser) stateFunc
// ParseParams rewrites the query from using "?" placeholders
// to using "@pN" parameter names that SQL Server will accept.
//
// This function and package is not subject to any API compatibility guarantee.
func ParseParams(query string) (string, int) {
p := &parser{
r: bytes.NewReader([]byte(query)),
namedParams: map[string]bool{},
}
state := parseNormal
for state != nil {
state = state(p)
}
return p.w.String(), p.paramMax + len(p.namedParams)
}
func parseNormal(p *parser) stateFunc {
for {
ch, ok := p.next()
if !ok {
return nil
}
if ch == '?' {
return parseOrdinalParameter
} else if ch == '$' || ch == ':' {
ch2, ok := p.next()
if !ok {
p.write(ch)
return nil
}
p.unread()
if ch2 >= '0' && ch2 <= '9' {
return parseOrdinalParameter
} else if 'a' <= ch2 && ch2 <= 'z' || 'A' <= ch2 && ch2 <= 'Z' {
return parseNamedParameter
}
}
p.write(ch)
switch ch {
case '\'':
return parseQuote
case '"':
return parseDoubleQuote
case '[':
return parseBracket
case '-':
return parseLineComment
case '/':
return parseComment
}
}
}
func parseOrdinalParameter(p *parser) stateFunc {
var paramN int
var ok bool
for {
var ch rune
ch, ok = p.next()
if ok && ch >= '0' && ch <= '9' {
paramN = paramN*10 + int(ch-'0')
} else {
break
}
}
if ok {
p.unread()
}
if paramN == 0 {
p.paramCount++
paramN = p.paramCount
}
if paramN > p.paramMax {
p.paramMax = paramN
}
p.w.WriteString("@p")
p.w.WriteString(strconv.Itoa(paramN))
if !ok {
return nil
}
return parseNormal
}
func parseNamedParameter(p *parser) stateFunc {
var paramName string
var ok bool
for {
var ch rune
ch, ok = p.next()
if ok && (ch >= '0' && ch <= '9' || 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z') {
paramName = paramName + string(ch)
} else {
break
}
}
if ok {
p.unread()
}
p.namedParams[paramName] = true
p.w.WriteString("@")
p.w.WriteString(paramName)
if !ok {
return nil
}
return parseNormal
}
func parseQuote(p *parser) stateFunc {
for {
ch, ok := p.next()
if !ok {
return nil
}
p.write(ch)
if ch == '\'' {
return parseNormal
}
}
}
func parseDoubleQuote(p *parser) stateFunc {
for {
ch, ok := p.next()
if !ok {
return nil
}
p.write(ch)
if ch == '"' {
return parseNormal
}
}
}
func parseBracket(p *parser) stateFunc {
for {
ch, ok := p.next()
if !ok {
return nil
}
p.write(ch)
if ch == ']' {
ch, ok = p.next()
if !ok {
return nil
}
if ch != ']' {
p.unread()
return parseNormal
}
p.write(ch)
}
}
}
func parseLineComment(p *parser) stateFunc {
ch, ok := p.next()
if !ok {
return nil
}
if ch != '-' {
p.unread()
return parseNormal
}
p.write(ch)
for {
ch, ok = p.next()
if !ok {
return nil
}
p.write(ch)
if ch == '\n' {
return parseNormal
}
}
}
func parseComment(p *parser) stateFunc {
var nested int
ch, ok := p.next()
if !ok {
return nil
}
if ch != '*' {
p.unread()
return parseNormal
}
p.write(ch)
for {
ch, ok = p.next()
if !ok {
return nil
}
p.write(ch)
for ch == '*' {
ch, ok = p.next()
if !ok {
return nil
}
p.write(ch)
if ch == '/' {
if nested == 0 {
return parseNormal
} else {
nested--
}
}
}
for ch == '/' {
ch, ok = p.next()
if !ok {
return nil
}
p.write(ch)
if ch == '*' {
nested++
}
}
}
}
-30
View File
@@ -1,30 +0,0 @@
package mssql
import (
"log"
)
type Logger interface {
Printf(format string, v ...interface{})
Println(v ...interface{})
}
type optionalLogger struct {
logger Logger
}
func (o optionalLogger) Printf(format string, v ...interface{}) {
if o.logger != nil {
o.logger.Printf(format, v...)
} else {
log.Printf(format, v...)
}
}
func (o optionalLogger) Println(v ...interface{}) {
if o.logger != nil {
o.logger.Println(v...)
} else {
log.Println(v...)
}
}
-957
View File
@@ -1,957 +0,0 @@
package mssql
import (
"context"
"database/sql"
"database/sql/driver"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"net"
"reflect"
"strings"
"time"
"unicode"
"github.com/denisenkom/go-mssqldb/internal/querytext"
)
// ReturnStatus may be used to return the return value from a proc.
//
// var rs mssql.ReturnStatus
// _, err := db.Exec("theproc", &rs)
// log.Printf("return status = %d", rs)
type ReturnStatus int32
var driverInstance = &Driver{processQueryText: true}
var driverInstanceNoProcess = &Driver{processQueryText: false}
func init() {
sql.Register("mssql", driverInstance)
sql.Register("sqlserver", driverInstanceNoProcess)
createDialer = func(p *connectParams) Dialer {
return netDialer{&net.Dialer{KeepAlive: p.keepAlive}}
}
}
var createDialer func(p *connectParams) Dialer
type netDialer struct {
nd *net.Dialer
}
func (d netDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
return d.nd.DialContext(ctx, network, addr)
}
type Driver struct {
log optionalLogger
processQueryText bool
}
// OpenConnector opens a new connector. Useful to dial with a context.
func (d *Driver) OpenConnector(dsn string) (*Connector, error) {
params, err := parseConnectParams(dsn)
if err != nil {
return nil, err
}
return &Connector{
params: params,
driver: d,
}, nil
}
func (d *Driver) Open(dsn string) (driver.Conn, error) {
return d.open(context.Background(), dsn)
}
func SetLogger(logger Logger) {
driverInstance.SetLogger(logger)
driverInstanceNoProcess.SetLogger(logger)
}
func (d *Driver) SetLogger(logger Logger) {
d.log = optionalLogger{logger}
}
// NewConnector creates a new connector from a DSN.
// The returned connector may be used with sql.OpenDB.
func NewConnector(dsn string) (*Connector, error) {
params, err := parseConnectParams(dsn)
if err != nil {
return nil, err
}
c := &Connector{
params: params,
driver: driverInstanceNoProcess,
}
return c, nil
}
// Connector holds the parsed DSN and is ready to make a new connection
// at any time.
//
// In the future, settings that cannot be passed through a string DSN
// may be set directly on the connector.
type Connector struct {
params connectParams
driver *Driver
// SessionInitSQL is executed after marking a given session to be reset.
// When not present, the next query will still reset the session to the
// database defaults.
//
// When present the connection will immediately mark the session to
// be reset, then execute the SessionInitSQL text to setup the session
// that may be different from the base database defaults.
//
// For Example, the application relies on the following defaults
// but is not allowed to set them at the database system level.
//
// SET XACT_ABORT ON;
// SET TEXTSIZE -1;
// SET ANSI_NULLS ON;
// SET LOCK_TIMEOUT 10000;
//
// SessionInitSQL should not attempt to manually call sp_reset_connection.
// This will happen at the TDS layer.
//
// SessionInitSQL is optional. The session will be reset even if
// SessionInitSQL is empty.
SessionInitSQL string
// Dialer sets a custom dialer for all network operations.
// If Dialer is not set, normal net dialers are used.
Dialer Dialer
}
type Dialer interface {
DialContext(ctx context.Context, network string, addr string) (net.Conn, error)
}
func (c *Connector) getDialer(p *connectParams) Dialer {
if c != nil && c.Dialer != nil {
return c.Dialer
}
return createDialer(p)
}
type Conn struct {
connector *Connector
sess *tdsSession
transactionCtx context.Context
resetSession bool
processQueryText bool
connectionGood bool
outs map[string]interface{}
returnStatus *ReturnStatus
}
func (c *Conn) setReturnStatus(s ReturnStatus) {
if c.returnStatus == nil {
return
}
*c.returnStatus = s
}
func (c *Conn) checkBadConn(err error) error {
// this is a hack to address Issue #275
// we set connectionGood flag to false if
// error indicates that connection is not usable
// but we return actual error instead of ErrBadConn
// this will cause connection to stay in a pool
// but next request to this connection will return ErrBadConn
// it might be possible to revise this hack after
// https://github.com/golang/go/issues/20807
// is implemented
switch err {
case nil:
return nil
case io.EOF:
c.connectionGood = false
return driver.ErrBadConn
case driver.ErrBadConn:
// It is an internal programming error if driver.ErrBadConn
// is ever passed to this function. driver.ErrBadConn should
// only ever be returned in response to a *mssql.Conn.connectionGood == false
// check in the external facing API.
panic("driver.ErrBadConn in checkBadConn. This should not happen.")
}
switch err.(type) {
case net.Error:
c.connectionGood = false
return err
case StreamError:
c.connectionGood = false
return err
default:
return err
}
}
func (c *Conn) clearOuts() {
c.outs = nil
}
func (c *Conn) simpleProcessResp(ctx context.Context) error {
tokchan := make(chan tokenStruct, 5)
go processResponse(ctx, c.sess, tokchan, c.outs)
c.clearOuts()
for tok := range tokchan {
switch token := tok.(type) {
case doneStruct:
if token.isError() {
return c.checkBadConn(token.getError())
}
case error:
return c.checkBadConn(token)
}
}
return nil
}
func (c *Conn) Commit() error {
if !c.connectionGood {
return driver.ErrBadConn
}
if err := c.sendCommitRequest(); err != nil {
return c.checkBadConn(err)
}
return c.simpleProcessResp(c.transactionCtx)
}
func (c *Conn) sendCommitRequest() error {
headers := []headerStruct{
{hdrtype: dataStmHdrTransDescr,
data: transDescrHdr{c.sess.tranid, 1}.pack()},
}
reset := c.resetSession
c.resetSession = false
if err := sendCommitXact(c.sess.buf, headers, "", 0, 0, "", reset); err != nil {
if c.sess.logFlags&logErrors != 0 {
c.sess.log.Printf("Failed to send CommitXact with %v", err)
}
c.connectionGood = false
return fmt.Errorf("Faild to send CommitXact: %v", err)
}
return nil
}
func (c *Conn) Rollback() error {
if !c.connectionGood {
return driver.ErrBadConn
}
if err := c.sendRollbackRequest(); err != nil {
return c.checkBadConn(err)
}
return c.simpleProcessResp(c.transactionCtx)
}
func (c *Conn) sendRollbackRequest() error {
headers := []headerStruct{
{hdrtype: dataStmHdrTransDescr,
data: transDescrHdr{c.sess.tranid, 1}.pack()},
}
reset := c.resetSession
c.resetSession = false
if err := sendRollbackXact(c.sess.buf, headers, "", 0, 0, "", reset); err != nil {
if c.sess.logFlags&logErrors != 0 {
c.sess.log.Printf("Failed to send RollbackXact with %v", err)
}
c.connectionGood = false
return fmt.Errorf("Failed to send RollbackXact: %v", err)
}
return nil
}
func (c *Conn) Begin() (driver.Tx, error) {
return c.begin(context.Background(), isolationUseCurrent)
}
func (c *Conn) begin(ctx context.Context, tdsIsolation isoLevel) (tx driver.Tx, err error) {
if !c.connectionGood {
return nil, driver.ErrBadConn
}
err = c.sendBeginRequest(ctx, tdsIsolation)
if err != nil {
return nil, c.checkBadConn(err)
}
tx, err = c.processBeginResponse(ctx)
if err != nil {
return nil, c.checkBadConn(err)
}
return
}
func (c *Conn) sendBeginRequest(ctx context.Context, tdsIsolation isoLevel) error {
c.transactionCtx = ctx
headers := []headerStruct{
{hdrtype: dataStmHdrTransDescr,
data: transDescrHdr{0, 1}.pack()},
}
reset := c.resetSession
c.resetSession = false
if err := sendBeginXact(c.sess.buf, headers, tdsIsolation, "", reset); err != nil {
if c.sess.logFlags&logErrors != 0 {
c.sess.log.Printf("Failed to send BeginXact with %v", err)
}
c.connectionGood = false
return fmt.Errorf("Failed to send BeginXact: %v", err)
}
return nil
}
func (c *Conn) processBeginResponse(ctx context.Context) (driver.Tx, error) {
if err := c.simpleProcessResp(ctx); err != nil {
return nil, err
}
// successful BEGINXACT request will return sess.tranid
// for started transaction
return c, nil
}
func (d *Driver) open(ctx context.Context, dsn string) (*Conn, error) {
params, err := parseConnectParams(dsn)
if err != nil {
return nil, err
}
return d.connect(ctx, nil, params)
}
// connect to the server, using the provided context for dialing only.
func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams) (*Conn, error) {
sess, err := connect(ctx, c, d.log, params)
if err != nil {
// main server failed, try fail-over partner
if params.failOverPartner == "" {
return nil, err
}
params.host = params.failOverPartner
if params.failOverPort != 0 {
params.port = params.failOverPort
}
sess, err = connect(ctx, c, d.log, params)
if err != nil {
// fail-over partner also failed, now fail
return nil, err
}
}
conn := &Conn{
connector: c,
sess: sess,
transactionCtx: context.Background(),
processQueryText: d.processQueryText,
connectionGood: true,
}
return conn, nil
}
func (c *Conn) Close() error {
return c.sess.buf.transport.Close()
}
type Stmt struct {
c *Conn
query string
paramCount int
notifSub *queryNotifSub
}
type queryNotifSub struct {
msgText string
options string
timeout uint32
}
func (c *Conn) Prepare(query string) (driver.Stmt, error) {
if !c.connectionGood {
return nil, driver.ErrBadConn
}
if len(query) > 10 && strings.EqualFold(query[:10], "INSERTBULK") {
return c.prepareCopyIn(context.Background(), query)
}
return c.prepareContext(context.Background(), query)
}
func (c *Conn) prepareContext(ctx context.Context, query string) (*Stmt, error) {
paramCount := -1
if c.processQueryText {
query, paramCount = querytext.ParseParams(query)
}
return &Stmt{c, query, paramCount, nil}, nil
}
func (s *Stmt) Close() error {
return nil
}
func (s *Stmt) SetQueryNotification(id, options string, timeout time.Duration) {
to := uint32(timeout / time.Second)
if to < 1 {
to = 1
}
s.notifSub = &queryNotifSub{id, options, to}
}
func (s *Stmt) NumInput() int {
return s.paramCount
}
func (s *Stmt) sendQuery(args []namedValue) (err error) {
headers := []headerStruct{
{hdrtype: dataStmHdrTransDescr,
data: transDescrHdr{s.c.sess.tranid, 1}.pack()},
}
if s.notifSub != nil {
headers = append(headers,
headerStruct{
hdrtype: dataStmHdrQueryNotif,
data: queryNotifHdr{
s.notifSub.msgText,
s.notifSub.options,
s.notifSub.timeout,
}.pack(),
})
}
conn := s.c
// no need to check number of parameters here, it is checked by database/sql
if conn.sess.logFlags&logSQL != 0 {
conn.sess.log.Println(s.query)
}
if conn.sess.logFlags&logParams != 0 && len(args) > 0 {
for i := 0; i < len(args); i++ {
if len(args[i].Name) > 0 {
s.c.sess.log.Printf("\t@%s\t%v\n", args[i].Name, args[i].Value)
} else {
s.c.sess.log.Printf("\t@p%d\t%v\n", i+1, args[i].Value)
}
}
}
reset := conn.resetSession
conn.resetSession = false
if len(args) == 0 {
if err = sendSqlBatch72(conn.sess.buf, s.query, headers, reset); err != nil {
if conn.sess.logFlags&logErrors != 0 {
conn.sess.log.Printf("Failed to send SqlBatch with %v", err)
}
conn.connectionGood = false
return fmt.Errorf("failed to send SQL Batch: %v", err)
}
} else {
proc := sp_ExecuteSql
var params []param
if isProc(s.query) {
proc.name = s.query
params, _, err = s.makeRPCParams(args, true)
if err != nil {
return
}
} else {
var decls []string
params, decls, err = s.makeRPCParams(args, false)
if err != nil {
return
}
params[0] = makeStrParam(s.query)
params[1] = makeStrParam(strings.Join(decls, ","))
}
if err = sendRpc(conn.sess.buf, headers, proc, 0, params, reset); err != nil {
if conn.sess.logFlags&logErrors != 0 {
conn.sess.log.Printf("Failed to send Rpc with %v", err)
}
conn.connectionGood = false
return fmt.Errorf("Failed to send RPC: %v", err)
}
}
return
}
// isProc takes the query text in s and determines if it is a stored proc name
// or SQL text.
func isProc(s string) bool {
if len(s) == 0 {
return false
}
const (
outside = iota
text
escaped
)
st := outside
var rn1, rPrev rune
for _, r := range s {
rPrev = rn1
rn1 = r
switch r {
// No newlines or string sequences.
case '\n', '\r', '\'', ';':
return false
}
switch st {
case outside:
switch {
case unicode.IsSpace(r):
return false
case r == '[':
st = escaped
continue
case r == ']' && rPrev == ']':
st = escaped
continue
case unicode.IsLetter(r):
st = text
}
case text:
switch {
case r == '.':
st = outside
continue
case unicode.IsSpace(r):
return false
}
case escaped:
switch {
case r == ']':
st = outside
continue
}
}
}
return true
}
func (s *Stmt) makeRPCParams(args []namedValue, isProc bool) ([]param, []string, error) {
var err error
var offset int
if !isProc {
offset = 2
}
params := make([]param, len(args)+offset)
decls := make([]string, len(args))
for i, val := range args {
params[i+offset], err = s.makeParam(val.Value)
if err != nil {
return nil, nil, err
}
var name string
if len(val.Name) > 0 {
name = "@" + val.Name
} else if !isProc {
name = fmt.Sprintf("@p%d", val.Ordinal)
}
params[i+offset].Name = name
decls[i] = fmt.Sprintf("%s %s", name, makeDecl(params[i+offset].ti))
}
return params, decls, nil
}
type namedValue struct {
Name string
Ordinal int
Value driver.Value
}
func convertOldArgs(args []driver.Value) []namedValue {
list := make([]namedValue, len(args))
for i, v := range args {
list[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
}
return list
}
func (s *Stmt) Query(args []driver.Value) (driver.Rows, error) {
return s.queryContext(context.Background(), convertOldArgs(args))
}
func (s *Stmt) queryContext(ctx context.Context, args []namedValue) (rows driver.Rows, err error) {
if !s.c.connectionGood {
return nil, driver.ErrBadConn
}
if err = s.sendQuery(args); err != nil {
return nil, s.c.checkBadConn(err)
}
return s.processQueryResponse(ctx)
}
func (s *Stmt) processQueryResponse(ctx context.Context) (res driver.Rows, err error) {
tokchan := make(chan tokenStruct, 5)
ctx, cancel := context.WithCancel(ctx)
go processResponse(ctx, s.c.sess, tokchan, s.c.outs)
s.c.clearOuts()
// process metadata
var cols []columnStruct
loop:
for tok := range tokchan {
switch token := tok.(type) {
// By ignoring DONE token we effectively
// skip empty result-sets.
// This improves results in queries like that:
// set nocount on; select 1
// see TestIgnoreEmptyResults test
//case doneStruct:
//break loop
case []columnStruct:
cols = token
break loop
case doneStruct:
if token.isError() {
return nil, s.c.checkBadConn(token.getError())
}
case ReturnStatus:
s.c.setReturnStatus(token)
case error:
return nil, s.c.checkBadConn(token)
}
}
res = &Rows{stmt: s, tokchan: tokchan, cols: cols, cancel: cancel}
return
}
func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) {
return s.exec(context.Background(), convertOldArgs(args))
}
func (s *Stmt) exec(ctx context.Context, args []namedValue) (res driver.Result, err error) {
if !s.c.connectionGood {
return nil, driver.ErrBadConn
}
if err = s.sendQuery(args); err != nil {
return nil, s.c.checkBadConn(err)
}
if res, err = s.processExec(ctx); err != nil {
return nil, s.c.checkBadConn(err)
}
return
}
func (s *Stmt) processExec(ctx context.Context) (res driver.Result, err error) {
tokchan := make(chan tokenStruct, 5)
go processResponse(ctx, s.c.sess, tokchan, s.c.outs)
s.c.clearOuts()
var rowCount int64
for token := range tokchan {
switch token := token.(type) {
case doneInProcStruct:
if token.Status&doneCount != 0 {
rowCount += int64(token.RowCount)
}
case doneStruct:
if token.Status&doneCount != 0 {
rowCount += int64(token.RowCount)
}
if token.isError() {
return nil, token.getError()
}
case ReturnStatus:
s.c.setReturnStatus(token)
case error:
return nil, token
}
}
return &Result{s.c, rowCount}, nil
}
type Rows struct {
stmt *Stmt
cols []columnStruct
tokchan chan tokenStruct
nextCols []columnStruct
cancel func()
}
func (rc *Rows) Close() error {
rc.cancel()
for _ = range rc.tokchan {
}
rc.tokchan = nil
return nil
}
func (rc *Rows) Columns() (res []string) {
res = make([]string, len(rc.cols))
for i, col := range rc.cols {
res[i] = col.ColName
}
return
}
func (rc *Rows) Next(dest []driver.Value) error {
if !rc.stmt.c.connectionGood {
return driver.ErrBadConn
}
if rc.nextCols != nil {
return io.EOF
}
for tok := range rc.tokchan {
switch tokdata := tok.(type) {
case []columnStruct:
rc.nextCols = tokdata
return io.EOF
case []interface{}:
for i := range dest {
dest[i] = tokdata[i]
}
return nil
case doneStruct:
if tokdata.isError() {
return rc.stmt.c.checkBadConn(tokdata.getError())
}
case ReturnStatus:
rc.stmt.c.setReturnStatus(tokdata)
case error:
return rc.stmt.c.checkBadConn(tokdata)
}
}
return io.EOF
}
func (rc *Rows) HasNextResultSet() bool {
return rc.nextCols != nil
}
func (rc *Rows) NextResultSet() error {
rc.cols = rc.nextCols
rc.nextCols = nil
if rc.cols == nil {
return io.EOF
}
return nil
}
// It should return
// the value type that can be used to scan types into. For example, the database
// column type "bigint" this should return "reflect.TypeOf(int64(0))".
func (r *Rows) ColumnTypeScanType(index int) reflect.Type {
return makeGoLangScanType(r.cols[index].ti)
}
// RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return the
// database system type name without the length. Type names should be uppercase.
// Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT",
// "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", "JSONB", "XML",
// "TIMESTAMP".
func (r *Rows) ColumnTypeDatabaseTypeName(index int) string {
return makeGoLangTypeName(r.cols[index].ti)
}
// RowsColumnTypeLength may be implemented by Rows. It should return the length
// of the column type if the column is a variable length type. If the column is
// not a variable length type ok should return false.
// If length is not limited other than system limits, it should return math.MaxInt64.
// The following are examples of returned values for various types:
// TEXT (math.MaxInt64, true)
// varchar(10) (10, true)
// nvarchar(10) (10, true)
// decimal (0, false)
// int (0, false)
// bytea(30) (30, true)
func (r *Rows) ColumnTypeLength(index int) (int64, bool) {
return makeGoLangTypeLength(r.cols[index].ti)
}
// It should return
// the precision and scale for decimal types. If not applicable, ok should be false.
// The following are examples of returned values for various types:
// decimal(38, 4) (38, 4, true)
// int (0, 0, false)
// decimal (math.MaxInt64, math.MaxInt64, true)
func (r *Rows) ColumnTypePrecisionScale(index int) (int64, int64, bool) {
return makeGoLangTypePrecisionScale(r.cols[index].ti)
}
// The nullable value should
// be true if it is known the column may be null, or false if the column is known
// to be not nullable.
// If the column nullability is unknown, ok should be false.
func (r *Rows) ColumnTypeNullable(index int) (nullable, ok bool) {
nullable = r.cols[index].Flags&colFlagNullable != 0
ok = true
return
}
func makeStrParam(val string) (res param) {
res.ti.TypeId = typeNVarChar
res.buffer = str2ucs2(val)
res.ti.Size = len(res.buffer)
return
}
func (s *Stmt) makeParam(val driver.Value) (res param, err error) {
if val == nil {
res.ti.TypeId = typeNull
res.buffer = nil
res.ti.Size = 0
return
}
switch val := val.(type) {
case int64:
res.ti.TypeId = typeIntN
res.buffer = make([]byte, 8)
res.ti.Size = 8
binary.LittleEndian.PutUint64(res.buffer, uint64(val))
case sql.NullInt64:
// only null values should be getting here
res.ti.TypeId = typeIntN
res.ti.Size = 8
res.buffer = []byte{}
case float64:
res.ti.TypeId = typeFltN
res.ti.Size = 8
res.buffer = make([]byte, 8)
binary.LittleEndian.PutUint64(res.buffer, math.Float64bits(val))
case sql.NullFloat64:
// only null values should be getting here
res.ti.TypeId = typeFltN
res.ti.Size = 8
res.buffer = []byte{}
case []byte:
res.ti.TypeId = typeBigVarBin
res.ti.Size = len(val)
res.buffer = val
case string:
res = makeStrParam(val)
case sql.NullString:
// only null values should be getting here
res.ti.TypeId = typeNVarChar
res.buffer = nil
res.ti.Size = 8000
case bool:
res.ti.TypeId = typeBitN
res.ti.Size = 1
res.buffer = make([]byte, 1)
if val {
res.buffer[0] = 1
}
case sql.NullBool:
// only null values should be getting here
res.ti.TypeId = typeBitN
res.ti.Size = 1
res.buffer = []byte{}
case time.Time:
if s.c.sess.loginAck.TDSVersion >= verTDS73 {
res.ti.TypeId = typeDateTimeOffsetN
res.ti.Scale = 7
res.buffer = encodeDateTimeOffset(val, int(res.ti.Scale))
res.ti.Size = len(res.buffer)
} else {
res.ti.TypeId = typeDateTimeN
res.buffer = encodeDateTime(val)
res.ti.Size = len(res.buffer)
}
default:
return s.makeParamExtra(val)
}
return
}
type Result struct {
c *Conn
rowsAffected int64
}
func (r *Result) RowsAffected() (int64, error) {
return r.rowsAffected, nil
}
var _ driver.Pinger = &Conn{}
// Ping is used to check if the remote server is available and satisfies the Pinger interface.
func (c *Conn) Ping(ctx context.Context) error {
if !c.connectionGood {
return driver.ErrBadConn
}
stmt := &Stmt{c, `select 1;`, 0, nil}
_, err := stmt.ExecContext(ctx, nil)
return err
}
var _ driver.ConnBeginTx = &Conn{}
// BeginTx satisfies ConnBeginTx.
func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if !c.connectionGood {
return nil, driver.ErrBadConn
}
if opts.ReadOnly {
return nil, errors.New("Read-only transactions are not supported")
}
var tdsIsolation isoLevel
switch sql.IsolationLevel(opts.Isolation) {
case sql.LevelDefault:
tdsIsolation = isolationUseCurrent
case sql.LevelReadUncommitted:
tdsIsolation = isolationReadUncommited
case sql.LevelReadCommitted:
tdsIsolation = isolationReadCommited
case sql.LevelWriteCommitted:
return nil, errors.New("LevelWriteCommitted isolation level is not supported")
case sql.LevelRepeatableRead:
tdsIsolation = isolationRepeatableRead
case sql.LevelSnapshot:
tdsIsolation = isolationSnapshot
case sql.LevelSerializable:
tdsIsolation = isolationSerializable
case sql.LevelLinearizable:
return nil, errors.New("LevelLinearizable isolation level is not supported")
default:
return nil, errors.New("Isolation level is not supported or unknown")
}
return c.begin(ctx, tdsIsolation)
}
func (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if !c.connectionGood {
return nil, driver.ErrBadConn
}
if len(query) > 10 && strings.EqualFold(query[:10], "INSERTBULK") {
return c.prepareCopyIn(ctx, query)
}
return c.prepareContext(ctx, query)
}
func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
if !s.c.connectionGood {
return nil, driver.ErrBadConn
}
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.queryContext(ctx, list)
}
func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
if !s.c.connectionGood {
return nil, driver.ErrBadConn
}
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.exec(ctx, list)
}
-52
View File
@@ -1,52 +0,0 @@
// +build go1.10
package mssql
import (
"context"
"database/sql/driver"
"errors"
)
var _ driver.Connector = &Connector{}
var _ driver.SessionResetter = &Conn{}
func (c *Conn) ResetSession(ctx context.Context) error {
if !c.connectionGood {
return driver.ErrBadConn
}
c.resetSession = true
if c.connector == nil || len(c.connector.SessionInitSQL) == 0 {
return nil
}
s, err := c.prepareContext(ctx, c.connector.SessionInitSQL)
if err != nil {
return driver.ErrBadConn
}
_, err = s.exec(ctx, nil)
if err != nil {
return driver.ErrBadConn
}
return nil
}
// Connect to the server and return a TDS connection.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
conn, err := c.driver.connect(ctx, c, c.params)
if err == nil {
err = conn.ResetSession(ctx)
}
return conn, err
}
// Driver underlying the Connector.
func (c *Connector) Driver() driver.Driver {
return c.driver
}
func (r *Result) LastInsertId() (int64, error) {
return -1, errors.New("LastInsertId is not supported. Please use the OUTPUT clause or add `select ID = convert(bigint, SCOPE_IDENTITY())` to the end of your query.")
}
-31
View File
@@ -1,31 +0,0 @@
// +build !go1.10
package mssql
import (
"database/sql/driver"
"errors"
)
func (r *Result) LastInsertId() (int64, error) {
s, err := r.c.Prepare("select cast(@@identity as bigint)")
if err != nil {
return 0, err
}
defer s.Close()
rows, err := s.Query(nil)
if err != nil {
return 0, err
}
defer rows.Close()
dest := make([]driver.Value, 1)
err = rows.Next(dest)
if err != nil {
return 0, err
}
if dest[0] == nil {
return -1, errors.New("There is no generated identity value")
}
lastInsertId := dest[0].(int64)
return lastInsertId, nil
}
-196
View File
@@ -1,196 +0,0 @@
// +build go1.9
package mssql
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"reflect"
"time"
// "github.com/cockroachdb/apd"
"github.com/golang-sql/civil"
)
// Type alias provided for compatibility.
type MssqlDriver = Driver // Deprecated: users should transition to the new name when possible.
type MssqlBulk = Bulk // Deprecated: users should transition to the new name when possible.
type MssqlBulkOptions = BulkOptions // Deprecated: users should transition to the new name when possible.
type MssqlConn = Conn // Deprecated: users should transition to the new name when possible.
type MssqlResult = Result // Deprecated: users should transition to the new name when possible.
type MssqlRows = Rows // Deprecated: users should transition to the new name when possible.
type MssqlStmt = Stmt // Deprecated: users should transition to the new name when possible.
var _ driver.NamedValueChecker = &Conn{}
// VarChar parameter types.
type VarChar string
type NVarCharMax string
type VarCharMax string
// DateTime1 encodes parameters to original DateTime SQL types.
type DateTime1 time.Time
// DateTimeOffset encodes parameters to DateTimeOffset, preserving the UTC offset.
type DateTimeOffset time.Time
func convertInputParameter(val interface{}) (interface{}, error) {
switch v := val.(type) {
case VarChar:
return val, nil
case NVarCharMax:
return val, nil
case VarCharMax:
return val, nil
case DateTime1:
return val, nil
case DateTimeOffset:
return val, nil
case civil.Date:
return val, nil
case civil.DateTime:
return val, nil
case civil.Time:
return val, nil
// case *apd.Decimal:
// return nil
default:
return driver.DefaultParameterConverter.ConvertValue(v)
}
}
func (c *Conn) CheckNamedValue(nv *driver.NamedValue) error {
switch v := nv.Value.(type) {
case sql.Out:
if c.outs == nil {
c.outs = make(map[string]interface{})
}
c.outs[nv.Name] = v.Dest
if v.Dest == nil {
return errors.New("destination is a nil pointer")
}
dest_info := reflect.ValueOf(v.Dest)
if dest_info.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
if dest_info.IsNil() {
return errors.New("destination is a nil pointer")
}
pointed_value := reflect.Indirect(dest_info)
// don't allow pointer to a pointer, only pointer to a value can be handled
// correctly
if pointed_value.Kind() == reflect.Ptr {
return errors.New("destination is a pointer to a pointer")
}
// Unwrap the Out value and check the inner value.
val := pointed_value.Interface()
if val == nil {
return errors.New("MSSQL does not allow NULL value without type for OUTPUT parameters")
}
conv, err := convertInputParameter(val)
if err != nil {
return err
}
if conv == nil {
// if we replace with nil we would lose type information
nv.Value = sql.Out{Dest: val}
} else {
nv.Value = sql.Out{Dest: conv}
}
return nil
case *ReturnStatus:
*v = 0 // By default the return value should be zero.
c.returnStatus = v
return driver.ErrRemoveArgument
case TVP:
return nil
default:
var err error
nv.Value, err = convertInputParameter(nv.Value)
return err
}
}
func (s *Stmt) makeParamExtra(val driver.Value) (res param, err error) {
switch val := val.(type) {
case VarChar:
res.ti.TypeId = typeBigVarChar
res.buffer = []byte(val)
res.ti.Size = len(res.buffer)
case VarCharMax:
res.ti.TypeId = typeBigVarChar
res.buffer = []byte(val)
res.ti.Size = 0 // currently zero forces varchar(max)
case NVarCharMax:
res.ti.TypeId = typeNVarChar
res.buffer = str2ucs2(string(val))
res.ti.Size = 0 // currently zero forces nvarchar(max)
case DateTime1:
t := time.Time(val)
res.ti.TypeId = typeDateTimeN
res.buffer = encodeDateTime(t)
res.ti.Size = len(res.buffer)
case DateTimeOffset:
res.ti.TypeId = typeDateTimeOffsetN
res.ti.Scale = 7
res.buffer = encodeDateTimeOffset(time.Time(val), int(res.ti.Scale))
res.ti.Size = len(res.buffer)
case civil.Date:
res.ti.TypeId = typeDateN
res.buffer = encodeDate(val.In(time.UTC))
res.ti.Size = len(res.buffer)
case civil.DateTime:
res.ti.TypeId = typeDateTime2N
res.ti.Scale = 7
res.buffer = encodeDateTime2(val.In(time.UTC), int(res.ti.Scale))
res.ti.Size = len(res.buffer)
case civil.Time:
res.ti.TypeId = typeTimeN
res.ti.Scale = 7
res.buffer = encodeTime(val.Hour, val.Minute, val.Second, val.Nanosecond, int(res.ti.Scale))
res.ti.Size = len(res.buffer)
case sql.Out:
res, err = s.makeParam(val.Dest)
res.Flags = fByRevValue
case TVP:
err = val.check()
if err != nil {
return
}
schema, name, errGetName := getSchemeAndName(val.TypeName)
if errGetName != nil {
return
}
res.ti.UdtInfo.TypeName = name
res.ti.UdtInfo.SchemaName = schema
res.ti.TypeId = typeTvp
columnStr, tvpFieldIndexes, errCalTypes := val.columnTypes()
if errCalTypes != nil {
err = errCalTypes
return
}
res.buffer, err = val.encode(schema, name, columnStr, tvpFieldIndexes)
if err != nil {
return
}
res.ti.Size = len(res.buffer)
default:
err = fmt.Errorf("mssql: unknown type for %T", val)
}
return
}
func scanIntoOut(name string, fromServer, scanInto interface{}) error {
return convertAssign(scanInto, fromServer)
}
-16
View File
@@ -1,16 +0,0 @@
// +build !go1.9
package mssql
import (
"database/sql/driver"
"fmt"
)
func (s *Stmt) makeParamExtra(val driver.Value) (param, error) {
return param{}, fmt.Errorf("mssql: unknown type for %T", val)
}
func scanIntoOut(name string, fromServer, scanInto interface{}) error {
return fmt.Errorf("mssql: unsupported OUTPUT type, use a newer Go version")
}
-168
View File
@@ -1,168 +0,0 @@
package mssql
import (
"fmt"
"net"
"time"
)
type timeoutConn struct {
c net.Conn
timeout time.Duration
}
func newTimeoutConn(conn net.Conn, timeout time.Duration) *timeoutConn {
return &timeoutConn{
c: conn,
timeout: timeout,
}
}
func (c *timeoutConn) Read(b []byte) (n int, err error) {
if c.timeout > 0 {
err = c.c.SetDeadline(time.Now().Add(c.timeout))
if err != nil {
return
}
}
return c.c.Read(b)
}
func (c *timeoutConn) Write(b []byte) (n int, err error) {
if c.timeout > 0 {
err = c.c.SetDeadline(time.Now().Add(c.timeout))
if err != nil {
return
}
}
return c.c.Write(b)
}
func (c timeoutConn) Close() error {
return c.c.Close()
}
func (c timeoutConn) LocalAddr() net.Addr {
return c.c.LocalAddr()
}
func (c timeoutConn) RemoteAddr() net.Addr {
return c.c.RemoteAddr()
}
func (c timeoutConn) SetDeadline(t time.Time) error {
panic("Not implemented")
}
func (c timeoutConn) SetReadDeadline(t time.Time) error {
panic("Not implemented")
}
func (c timeoutConn) SetWriteDeadline(t time.Time) error {
panic("Not implemented")
}
// this connection is used during TLS Handshake
// TDS protocol requires TLS handshake messages to be sent inside TDS packets
type tlsHandshakeConn struct {
buf *tdsBuffer
packetPending bool
continueRead bool
}
func (c *tlsHandshakeConn) Read(b []byte) (n int, err error) {
if c.packetPending {
c.packetPending = false
err = c.buf.FinishPacket()
if err != nil {
err = fmt.Errorf("Cannot send handshake packet: %s", err.Error())
return
}
c.continueRead = false
}
if !c.continueRead {
var packet packetType
packet, err = c.buf.BeginRead()
if err != nil {
err = fmt.Errorf("Cannot read handshake packet: %s", err.Error())
return
}
if packet != packPrelogin {
err = fmt.Errorf("unexpected packet %d, expecting prelogin", packet)
return
}
c.continueRead = true
}
return c.buf.Read(b)
}
func (c *tlsHandshakeConn) Write(b []byte) (n int, err error) {
if !c.packetPending {
c.buf.BeginPacket(packPrelogin, false)
c.packetPending = true
}
return c.buf.Write(b)
}
func (c *tlsHandshakeConn) Close() error {
panic("Not implemented")
}
func (c *tlsHandshakeConn) LocalAddr() net.Addr {
panic("Not implemented")
}
func (c *tlsHandshakeConn) RemoteAddr() net.Addr {
panic("Not implemented")
}
func (c *tlsHandshakeConn) SetDeadline(t time.Time) error {
panic("Not implemented")
}
func (c *tlsHandshakeConn) SetReadDeadline(t time.Time) error {
panic("Not implemented")
}
func (c *tlsHandshakeConn) SetWriteDeadline(t time.Time) error {
panic("Not implemented")
}
// this connection just delegates all methods to it's wrapped connection
// it also allows switching underlying connection on the fly
// it is needed because tls.Conn does not allow switching underlying connection
type passthroughConn struct {
c net.Conn
}
func (c passthroughConn) Read(b []byte) (n int, err error) {
return c.c.Read(b)
}
func (c passthroughConn) Write(b []byte) (n int, err error) {
return c.c.Write(b)
}
func (c passthroughConn) Close() error {
return c.c.Close()
}
func (c passthroughConn) LocalAddr() net.Addr {
panic("Not implemented")
}
func (c passthroughConn) RemoteAddr() net.Addr {
panic("Not implemented")
}
func (c passthroughConn) SetDeadline(t time.Time) error {
panic("Not implemented")
}
func (c passthroughConn) SetReadDeadline(t time.Time) error {
panic("Not implemented")
}
func (c passthroughConn) SetWriteDeadline(t time.Time) error {
panic("Not implemented")
}
-283
View File
@@ -1,283 +0,0 @@
// +build !windows
package mssql
import (
"crypto/des"
"crypto/md5"
"crypto/rand"
"encoding/binary"
"errors"
"strings"
"unicode/utf16"
"golang.org/x/crypto/md4"
)
const (
_NEGOTIATE_MESSAGE = 1
_CHALLENGE_MESSAGE = 2
_AUTHENTICATE_MESSAGE = 3
)
const (
_NEGOTIATE_UNICODE = 0x00000001
_NEGOTIATE_OEM = 0x00000002
_NEGOTIATE_TARGET = 0x00000004
_NEGOTIATE_SIGN = 0x00000010
_NEGOTIATE_SEAL = 0x00000020
_NEGOTIATE_DATAGRAM = 0x00000040
_NEGOTIATE_LMKEY = 0x00000080
_NEGOTIATE_NTLM = 0x00000200
_NEGOTIATE_ANONYMOUS = 0x00000800
_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
_NEGOTIATE_ALWAYS_SIGN = 0x00008000
_NEGOTIATE_TARGET_TYPE_DOMAIN = 0x00010000
_NEGOTIATE_TARGET_TYPE_SERVER = 0x00020000
_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
_NEGOTIATE_IDENTIFY = 0x00100000
_REQUEST_NON_NT_SESSION_KEY = 0x00400000
_NEGOTIATE_TARGET_INFO = 0x00800000
_NEGOTIATE_VERSION = 0x02000000
_NEGOTIATE_128 = 0x20000000
_NEGOTIATE_KEY_EXCH = 0x40000000
_NEGOTIATE_56 = 0x80000000
)
const _NEGOTIATE_FLAGS = _NEGOTIATE_UNICODE |
_NEGOTIATE_NTLM |
_NEGOTIATE_OEM_DOMAIN_SUPPLIED |
_NEGOTIATE_OEM_WORKSTATION_SUPPLIED |
_NEGOTIATE_ALWAYS_SIGN |
_NEGOTIATE_EXTENDED_SESSIONSECURITY
type ntlmAuth struct {
Domain string
UserName string
Password string
Workstation string
}
func getAuth(user, password, service, workstation string) (auth, bool) {
if !strings.ContainsRune(user, '\\') {
return nil, false
}
domain_user := strings.SplitN(user, "\\", 2)
return &ntlmAuth{
Domain: domain_user[0],
UserName: domain_user[1],
Password: password,
Workstation: workstation,
}, true
}
func utf16le(val string) []byte {
var v []byte
for _, r := range val {
if utf16.IsSurrogate(r) {
r1, r2 := utf16.EncodeRune(r)
v = append(v, byte(r1), byte(r1>>8))
v = append(v, byte(r2), byte(r2>>8))
} else {
v = append(v, byte(r), byte(r>>8))
}
}
return v
}
func (auth *ntlmAuth) InitialBytes() ([]byte, error) {
domain_len := len(auth.Domain)
workstation_len := len(auth.Workstation)
msg := make([]byte, 40+domain_len+workstation_len)
copy(msg, []byte("NTLMSSP\x00"))
binary.LittleEndian.PutUint32(msg[8:], _NEGOTIATE_MESSAGE)
binary.LittleEndian.PutUint32(msg[12:], _NEGOTIATE_FLAGS)
// Domain Name Fields
binary.LittleEndian.PutUint16(msg[16:], uint16(domain_len))
binary.LittleEndian.PutUint16(msg[18:], uint16(domain_len))
binary.LittleEndian.PutUint32(msg[20:], 40)
// Workstation Fields
binary.LittleEndian.PutUint16(msg[24:], uint16(workstation_len))
binary.LittleEndian.PutUint16(msg[26:], uint16(workstation_len))
binary.LittleEndian.PutUint32(msg[28:], uint32(40+domain_len))
// Version
binary.LittleEndian.PutUint32(msg[32:], 0)
binary.LittleEndian.PutUint32(msg[36:], 0)
// Payload
copy(msg[40:], auth.Domain)
copy(msg[40+domain_len:], auth.Workstation)
return msg, nil
}
var errorNTLM = errors.New("NTLM protocol error")
func createDesKey(bytes, material []byte) {
material[0] = bytes[0]
material[1] = (byte)(bytes[0]<<7 | (bytes[1]&0xff)>>1)
material[2] = (byte)(bytes[1]<<6 | (bytes[2]&0xff)>>2)
material[3] = (byte)(bytes[2]<<5 | (bytes[3]&0xff)>>3)
material[4] = (byte)(bytes[3]<<4 | (bytes[4]&0xff)>>4)
material[5] = (byte)(bytes[4]<<3 | (bytes[5]&0xff)>>5)
material[6] = (byte)(bytes[5]<<2 | (bytes[6]&0xff)>>6)
material[7] = (byte)(bytes[6] << 1)
}
func oddParity(bytes []byte) {
for i := 0; i < len(bytes); i++ {
b := bytes[i]
needsParity := (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0
if needsParity {
bytes[i] = bytes[i] | byte(0x01)
} else {
bytes[i] = bytes[i] & byte(0xfe)
}
}
}
func encryptDes(key []byte, cleartext []byte, ciphertext []byte) {
var desKey [8]byte
createDesKey(key, desKey[:])
cipher, err := des.NewCipher(desKey[:])
if err != nil {
panic(err)
}
cipher.Encrypt(ciphertext, cleartext)
}
func response(challenge [8]byte, hash [21]byte) (ret [24]byte) {
encryptDes(hash[:7], challenge[:], ret[:8])
encryptDes(hash[7:14], challenge[:], ret[8:16])
encryptDes(hash[14:], challenge[:], ret[16:])
return
}
func lmHash(password string) (hash [21]byte) {
var lmpass [14]byte
copy(lmpass[:14], []byte(strings.ToUpper(password)))
magic := []byte("KGS!@#$%")
encryptDes(lmpass[:7], magic, hash[:8])
encryptDes(lmpass[7:], magic, hash[8:])
return
}
func lmResponse(challenge [8]byte, password string) [24]byte {
hash := lmHash(password)
return response(challenge, hash)
}
func ntlmHash(password string) (hash [21]byte) {
h := md4.New()
h.Write(utf16le(password))
h.Sum(hash[:0])
return
}
func ntResponse(challenge [8]byte, password string) [24]byte {
hash := ntlmHash(password)
return response(challenge, hash)
}
func clientChallenge() (nonce [8]byte) {
_, err := rand.Read(nonce[:])
if err != nil {
panic(err)
}
return
}
func ntlmSessionResponse(clientNonce [8]byte, serverChallenge [8]byte, password string) [24]byte {
var sessionHash [16]byte
h := md5.New()
h.Write(serverChallenge[:])
h.Write(clientNonce[:])
h.Sum(sessionHash[:0])
var hash [8]byte
copy(hash[:], sessionHash[:8])
passwordHash := ntlmHash(password)
return response(hash, passwordHash)
}
func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) {
if string(bytes[0:8]) != "NTLMSSP\x00" {
return nil, errorNTLM
}
if binary.LittleEndian.Uint32(bytes[8:12]) != _CHALLENGE_MESSAGE {
return nil, errorNTLM
}
flags := binary.LittleEndian.Uint32(bytes[20:24])
var challenge [8]byte
copy(challenge[:], bytes[24:32])
var lm, nt []byte
if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 {
nonce := clientChallenge()
var lm_bytes [24]byte
copy(lm_bytes[:8], nonce[:])
lm = lm_bytes[:]
nt_bytes := ntlmSessionResponse(nonce, challenge, auth.Password)
nt = nt_bytes[:]
} else {
lm_bytes := lmResponse(challenge, auth.Password)
lm = lm_bytes[:]
nt_bytes := ntResponse(challenge, auth.Password)
nt = nt_bytes[:]
}
lm_len := len(lm)
nt_len := len(nt)
domain16 := utf16le(auth.Domain)
domain_len := len(domain16)
user16 := utf16le(auth.UserName)
user_len := len(user16)
workstation16 := utf16le(auth.Workstation)
workstation_len := len(workstation16)
msg := make([]byte, 88+lm_len+nt_len+domain_len+user_len+workstation_len)
copy(msg, []byte("NTLMSSP\x00"))
binary.LittleEndian.PutUint32(msg[8:], _AUTHENTICATE_MESSAGE)
// Lm Challenge Response Fields
binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len))
binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len))
binary.LittleEndian.PutUint32(msg[16:], 88)
// Nt Challenge Response Fields
binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len))
binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len))
binary.LittleEndian.PutUint32(msg[24:], uint32(88+lm_len))
// Domain Name Fields
binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len))
binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len))
binary.LittleEndian.PutUint32(msg[32:], uint32(88+lm_len+nt_len))
// User Name Fields
binary.LittleEndian.PutUint16(msg[36:], uint16(user_len))
binary.LittleEndian.PutUint16(msg[38:], uint16(user_len))
binary.LittleEndian.PutUint32(msg[40:], uint32(88+lm_len+nt_len+domain_len))
// Workstation Fields
binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len))
binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len))
binary.LittleEndian.PutUint32(msg[48:], uint32(88+lm_len+nt_len+domain_len+user_len))
// Encrypted Random Session Key Fields
binary.LittleEndian.PutUint16(msg[52:], 0)
binary.LittleEndian.PutUint16(msg[54:], 0)
binary.LittleEndian.PutUint32(msg[56:], uint32(88+lm_len+nt_len+domain_len+user_len+workstation_len))
// Negotiate Flags
binary.LittleEndian.PutUint32(msg[60:], flags)
// Version
binary.LittleEndian.PutUint32(msg[64:], 0)
binary.LittleEndian.PutUint32(msg[68:], 0)
// MIC
binary.LittleEndian.PutUint32(msg[72:], 0)
binary.LittleEndian.PutUint32(msg[76:], 0)
binary.LittleEndian.PutUint32(msg[88:], 0)
binary.LittleEndian.PutUint32(msg[84:], 0)
// Payload
copy(msg[88:], lm)
copy(msg[88+lm_len:], nt)
copy(msg[88+lm_len+nt_len:], domain16)
copy(msg[88+lm_len+nt_len+domain_len:], user16)
copy(msg[88+lm_len+nt_len+domain_len+user_len:], workstation16)
return msg, nil
}
func (auth *ntlmAuth) Free() {
}
-89
View File
@@ -1,89 +0,0 @@
package mssql
import (
"encoding/binary"
)
type procId struct {
id uint16
name string
}
// parameter flags
const (
fByRevValue = 1
fDefaultValue = 2
)
type param struct {
Name string
Flags uint8
ti typeInfo
buffer []byte
}
const (
fWithRecomp = 1
fNoMetaData = 2
fReuseMetaData = 4
)
var (
sp_Cursor = procId{1, ""}
sp_CursorOpen = procId{2, ""}
sp_CursorPrepare = procId{3, ""}
sp_CursorExecute = procId{4, ""}
sp_CursorPrepExec = procId{5, ""}
sp_CursorUnprepare = procId{6, ""}
sp_CursorFetch = procId{7, ""}
sp_CursorOption = procId{8, ""}
sp_CursorClose = procId{9, ""}
sp_ExecuteSql = procId{10, ""}
sp_Prepare = procId{11, ""}
sp_PrepExec = procId{13, ""}
sp_PrepExecRpc = procId{14, ""}
sp_Unprepare = procId{15, ""}
)
// http://msdn.microsoft.com/en-us/library/dd357576.aspx
func sendRpc(buf *tdsBuffer, headers []headerStruct, proc procId, flags uint16, params []param, resetSession bool) (err error) {
buf.BeginPacket(packRPCRequest, resetSession)
writeAllHeaders(buf, headers)
if len(proc.name) == 0 {
var idswitch uint16 = 0xffff
err = binary.Write(buf, binary.LittleEndian, &idswitch)
if err != nil {
return
}
err = binary.Write(buf, binary.LittleEndian, &proc.id)
if err != nil {
return
}
} else {
err = writeUsVarChar(buf, proc.name)
if err != nil {
return
}
}
err = binary.Write(buf, binary.LittleEndian, &flags)
if err != nil {
return
}
for _, param := range params {
if err = writeBVarChar(buf, param.Name); err != nil {
return
}
if err = binary.Write(buf, binary.LittleEndian, param.Flags); err != nil {
return
}
err = writeTypeInfo(buf, &param.ti)
if err != nil {
return
}
err = param.ti.Writer(buf, param.ti, param.buffer)
if err != nil {
return
}
}
return buf.FinishPacket()
}
-266
View File
@@ -1,266 +0,0 @@
package mssql
import (
"fmt"
"strings"
"syscall"
"unsafe"
)
var (
secur32_dll = syscall.NewLazyDLL("secur32.dll")
initSecurityInterface = secur32_dll.NewProc("InitSecurityInterfaceW")
sec_fn *SecurityFunctionTable
)
func init() {
ptr, _, _ := initSecurityInterface.Call()
sec_fn = (*SecurityFunctionTable)(unsafe.Pointer(ptr))
}
const (
SEC_E_OK = 0
SECPKG_CRED_OUTBOUND = 2
SEC_WINNT_AUTH_IDENTITY_UNICODE = 2
ISC_REQ_DELEGATE = 0x00000001
ISC_REQ_REPLAY_DETECT = 0x00000004
ISC_REQ_SEQUENCE_DETECT = 0x00000008
ISC_REQ_CONFIDENTIALITY = 0x00000010
ISC_REQ_CONNECTION = 0x00000800
SECURITY_NETWORK_DREP = 0
SEC_I_CONTINUE_NEEDED = 0x00090312
SEC_I_COMPLETE_NEEDED = 0x00090313
SEC_I_COMPLETE_AND_CONTINUE = 0x00090314
SECBUFFER_VERSION = 0
SECBUFFER_TOKEN = 2
NTLMBUF_LEN = 12000
)
const ISC_REQ = ISC_REQ_CONFIDENTIALITY |
ISC_REQ_REPLAY_DETECT |
ISC_REQ_SEQUENCE_DETECT |
ISC_REQ_CONNECTION |
ISC_REQ_DELEGATE
type SecurityFunctionTable struct {
dwVersion uint32
EnumerateSecurityPackages uintptr
QueryCredentialsAttributes uintptr
AcquireCredentialsHandle uintptr
FreeCredentialsHandle uintptr
Reserved2 uintptr
InitializeSecurityContext uintptr
AcceptSecurityContext uintptr
CompleteAuthToken uintptr
DeleteSecurityContext uintptr
ApplyControlToken uintptr
QueryContextAttributes uintptr
ImpersonateSecurityContext uintptr
RevertSecurityContext uintptr
MakeSignature uintptr
VerifySignature uintptr
FreeContextBuffer uintptr
QuerySecurityPackageInfo uintptr
Reserved3 uintptr
Reserved4 uintptr
Reserved5 uintptr
Reserved6 uintptr
Reserved7 uintptr
Reserved8 uintptr
QuerySecurityContextToken uintptr
EncryptMessage uintptr
DecryptMessage uintptr
}
type SEC_WINNT_AUTH_IDENTITY struct {
User *uint16
UserLength uint32
Domain *uint16
DomainLength uint32
Password *uint16
PasswordLength uint32
Flags uint32
}
type TimeStamp struct {
LowPart uint32
HighPart int32
}
type SecHandle struct {
dwLower uintptr
dwUpper uintptr
}
type SecBuffer struct {
cbBuffer uint32
BufferType uint32
pvBuffer *byte
}
type SecBufferDesc struct {
ulVersion uint32
cBuffers uint32
pBuffers *SecBuffer
}
type SSPIAuth struct {
Domain string
UserName string
Password string
Service string
cred SecHandle
ctxt SecHandle
}
func getAuth(user, password, service, workstation string) (auth, bool) {
if user == "" {
return &SSPIAuth{Service: service}, true
}
if !strings.ContainsRune(user, '\\') {
return nil, false
}
domain_user := strings.SplitN(user, "\\", 2)
return &SSPIAuth{
Domain: domain_user[0],
UserName: domain_user[1],
Password: password,
Service: service,
}, true
}
func (auth *SSPIAuth) InitialBytes() ([]byte, error) {
var identity *SEC_WINNT_AUTH_IDENTITY
if auth.UserName != "" {
identity = &SEC_WINNT_AUTH_IDENTITY{
Flags: SEC_WINNT_AUTH_IDENTITY_UNICODE,
Password: syscall.StringToUTF16Ptr(auth.Password),
PasswordLength: uint32(len(auth.Password)),
Domain: syscall.StringToUTF16Ptr(auth.Domain),
DomainLength: uint32(len(auth.Domain)),
User: syscall.StringToUTF16Ptr(auth.UserName),
UserLength: uint32(len(auth.UserName)),
}
}
var ts TimeStamp
sec_ok, _, _ := syscall.Syscall9(sec_fn.AcquireCredentialsHandle,
9,
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Negotiate"))),
SECPKG_CRED_OUTBOUND,
0,
uintptr(unsafe.Pointer(identity)),
0,
0,
uintptr(unsafe.Pointer(&auth.cred)),
uintptr(unsafe.Pointer(&ts)))
if sec_ok != SEC_E_OK {
return nil, fmt.Errorf("AcquireCredentialsHandle failed %x", sec_ok)
}
var buf SecBuffer
var desc SecBufferDesc
desc.ulVersion = SECBUFFER_VERSION
desc.cBuffers = 1
desc.pBuffers = &buf
outbuf := make([]byte, NTLMBUF_LEN)
buf.cbBuffer = NTLMBUF_LEN
buf.BufferType = SECBUFFER_TOKEN
buf.pvBuffer = &outbuf[0]
var attrs uint32
sec_ok, _, _ = syscall.Syscall12(sec_fn.InitializeSecurityContext,
12,
uintptr(unsafe.Pointer(&auth.cred)),
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
ISC_REQ,
0,
SECURITY_NETWORK_DREP,
0,
0,
uintptr(unsafe.Pointer(&auth.ctxt)),
uintptr(unsafe.Pointer(&desc)),
uintptr(unsafe.Pointer(&attrs)),
uintptr(unsafe.Pointer(&ts)))
if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
sec_ok == SEC_I_COMPLETE_NEEDED {
syscall.Syscall6(sec_fn.CompleteAuthToken,
2,
uintptr(unsafe.Pointer(&auth.ctxt)),
uintptr(unsafe.Pointer(&desc)),
0, 0, 0, 0)
} else if sec_ok != SEC_E_OK &&
sec_ok != SEC_I_CONTINUE_NEEDED {
syscall.Syscall6(sec_fn.FreeCredentialsHandle,
1,
uintptr(unsafe.Pointer(&auth.cred)),
0, 0, 0, 0, 0)
return nil, fmt.Errorf("InitialBytes InitializeSecurityContext failed %x", sec_ok)
}
return outbuf[:buf.cbBuffer], nil
}
func (auth *SSPIAuth) NextBytes(bytes []byte) ([]byte, error) {
var in_buf, out_buf SecBuffer
var in_desc, out_desc SecBufferDesc
in_desc.ulVersion = SECBUFFER_VERSION
in_desc.cBuffers = 1
in_desc.pBuffers = &in_buf
out_desc.ulVersion = SECBUFFER_VERSION
out_desc.cBuffers = 1
out_desc.pBuffers = &out_buf
in_buf.BufferType = SECBUFFER_TOKEN
in_buf.pvBuffer = &bytes[0]
in_buf.cbBuffer = uint32(len(bytes))
outbuf := make([]byte, NTLMBUF_LEN)
out_buf.BufferType = SECBUFFER_TOKEN
out_buf.pvBuffer = &outbuf[0]
out_buf.cbBuffer = NTLMBUF_LEN
var attrs uint32
var ts TimeStamp
sec_ok, _, _ := syscall.Syscall12(sec_fn.InitializeSecurityContext,
12,
uintptr(unsafe.Pointer(&auth.cred)),
uintptr(unsafe.Pointer(&auth.ctxt)),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
ISC_REQ,
0,
SECURITY_NETWORK_DREP,
uintptr(unsafe.Pointer(&in_desc)),
0,
uintptr(unsafe.Pointer(&auth.ctxt)),
uintptr(unsafe.Pointer(&out_desc)),
uintptr(unsafe.Pointer(&attrs)),
uintptr(unsafe.Pointer(&ts)))
if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
sec_ok == SEC_I_COMPLETE_NEEDED {
syscall.Syscall6(sec_fn.CompleteAuthToken,
2,
uintptr(unsafe.Pointer(&auth.ctxt)),
uintptr(unsafe.Pointer(&out_desc)),
0, 0, 0, 0)
} else if sec_ok != SEC_E_OK &&
sec_ok != SEC_I_CONTINUE_NEEDED {
return nil, fmt.Errorf("NextBytes InitializeSecurityContext failed %x", sec_ok)
}
return outbuf[:out_buf.cbBuffer], nil
}
func (auth *SSPIAuth) Free() {
syscall.Syscall6(sec_fn.DeleteSecurityContext,
1,
uintptr(unsafe.Pointer(&auth.ctxt)),
0, 0, 0, 0, 0)
syscall.Syscall6(sec_fn.FreeCredentialsHandle,
1,
uintptr(unsafe.Pointer(&auth.cred)),
0, 0, 0, 0, 0)
}
-914
View File
@@ -1,914 +0,0 @@
package mssql
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"sort"
"strconv"
"strings"
"unicode/utf16"
"unicode/utf8"
)
func parseInstances(msg []byte) map[string]map[string]string {
results := map[string]map[string]string{}
if len(msg) > 3 && msg[0] == 5 {
out_s := string(msg[3:])
tokens := strings.Split(out_s, ";")
instdict := map[string]string{}
got_name := false
var name string
for _, token := range tokens {
if got_name {
instdict[name] = token
got_name = false
} else {
name = token
if len(name) == 0 {
if len(instdict) == 0 {
break
}
results[strings.ToUpper(instdict["InstanceName"])] = instdict
instdict = map[string]string{}
continue
}
got_name = true
}
}
}
return results
}
func getInstances(ctx context.Context, d Dialer, address string) (map[string]map[string]string, error) {
conn, err := d.DialContext(ctx, "udp", address+":1434")
if err != nil {
return nil, err
}
defer conn.Close()
deadline, _ := ctx.Deadline()
conn.SetDeadline(deadline)
_, err = conn.Write([]byte{3})
if err != nil {
return nil, err
}
var resp = make([]byte, 16*1024-1)
read, err := conn.Read(resp)
if err != nil {
return nil, err
}
return parseInstances(resp[:read]), nil
}
// tds versions
const (
verTDS70 = 0x70000000
verTDS71 = 0x71000000
verTDS71rev1 = 0x71000001
verTDS72 = 0x72090002
verTDS73A = 0x730A0003
verTDS73 = verTDS73A
verTDS73B = 0x730B0003
verTDS74 = 0x74000004
)
// packet types
// https://msdn.microsoft.com/en-us/library/dd304214.aspx
const (
packSQLBatch packetType = 1
packRPCRequest = 3
packReply = 4
// 2.2.1.7 Attention: https://msdn.microsoft.com/en-us/library/dd341449.aspx
// 4.19.2 Out-of-Band Attention Signal: https://msdn.microsoft.com/en-us/library/dd305167.aspx
packAttention = 6
packBulkLoadBCP = 7
packTransMgrReq = 14
packNormal = 15
packLogin7 = 16
packSSPIMessage = 17
packPrelogin = 18
)
// prelogin fields
// http://msdn.microsoft.com/en-us/library/dd357559.aspx
const (
preloginVERSION = 0
preloginENCRYPTION = 1
preloginINSTOPT = 2
preloginTHREADID = 3
preloginMARS = 4
preloginTRACEID = 5
preloginTERMINATOR = 0xff
)
const (
encryptOff = 0 // Encryption is available but off.
encryptOn = 1 // Encryption is available and on.
encryptNotSup = 2 // Encryption is not available.
encryptReq = 3 // Encryption is required.
)
type tdsSession struct {
buf *tdsBuffer
loginAck loginAckStruct
database string
partner string
columns []columnStruct
tranid uint64
logFlags uint64
log optionalLogger
routedServer string
routedPort uint16
}
const (
logErrors = 1
logMessages = 2
logRows = 4
logSQL = 8
logParams = 16
logTransaction = 32
logDebug = 64
)
type columnStruct struct {
UserType uint32
Flags uint16
ColName string
ti typeInfo
}
type keySlice []uint8
func (p keySlice) Len() int { return len(p) }
func (p keySlice) Less(i, j int) bool { return p[i] < p[j] }
func (p keySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// http://msdn.microsoft.com/en-us/library/dd357559.aspx
func writePrelogin(w *tdsBuffer, fields map[uint8][]byte) error {
var err error
w.BeginPacket(packPrelogin, false)
offset := uint16(5*len(fields) + 1)
keys := make(keySlice, 0, len(fields))
for k, _ := range fields {
keys = append(keys, k)
}
sort.Sort(keys)
// writing header
for _, k := range keys {
err = w.WriteByte(k)
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, offset)
if err != nil {
return err
}
v := fields[k]
size := uint16(len(v))
err = binary.Write(w, binary.BigEndian, size)
if err != nil {
return err
}
offset += size
}
err = w.WriteByte(preloginTERMINATOR)
if err != nil {
return err
}
// writing values
for _, k := range keys {
v := fields[k]
written, err := w.Write(v)
if err != nil {
return err
}
if written != len(v) {
return errors.New("Write method didn't write the whole value")
}
}
return w.FinishPacket()
}
func readPrelogin(r *tdsBuffer) (map[uint8][]byte, error) {
packet_type, err := r.BeginRead()
if err != nil {
return nil, err
}
struct_buf, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
if packet_type != 4 {
return nil, errors.New("Invalid respones, expected packet type 4, PRELOGIN RESPONSE")
}
offset := 0
results := map[uint8][]byte{}
for true {
rec_type := struct_buf[offset]
if rec_type == preloginTERMINATOR {
break
}
rec_offset := binary.BigEndian.Uint16(struct_buf[offset+1:])
rec_len := binary.BigEndian.Uint16(struct_buf[offset+3:])
value := struct_buf[rec_offset : rec_offset+rec_len]
results[rec_type] = value
offset += 5
}
return results, nil
}
// OptionFlags2
// http://msdn.microsoft.com/en-us/library/dd304019.aspx
const (
fLanguageFatal = 1
fODBC = 2
fTransBoundary = 4
fCacheConnect = 8
fIntSecurity = 0x80
)
// TypeFlags
const (
// 4 bits for fSQLType
// 1 bit for fOLEDB
fReadOnlyIntent = 32
)
type login struct {
TDSVersion uint32
PacketSize uint32
ClientProgVer uint32
ClientPID uint32
ConnectionID uint32
OptionFlags1 uint8
OptionFlags2 uint8
TypeFlags uint8
OptionFlags3 uint8
ClientTimeZone int32
ClientLCID uint32
HostName string
UserName string
Password string
AppName string
ServerName string
CtlIntName string
Language string
Database string
ClientID [6]byte
SSPI []byte
AtchDBFile string
ChangePassword string
}
type loginHeader struct {
Length uint32
TDSVersion uint32
PacketSize uint32
ClientProgVer uint32
ClientPID uint32
ConnectionID uint32
OptionFlags1 uint8
OptionFlags2 uint8
TypeFlags uint8
OptionFlags3 uint8
ClientTimeZone int32
ClientLCID uint32
HostNameOffset uint16
HostNameLength uint16
UserNameOffset uint16
UserNameLength uint16
PasswordOffset uint16
PasswordLength uint16
AppNameOffset uint16
AppNameLength uint16
ServerNameOffset uint16
ServerNameLength uint16
ExtensionOffset uint16
ExtensionLenght uint16
CtlIntNameOffset uint16
CtlIntNameLength uint16
LanguageOffset uint16
LanguageLength uint16
DatabaseOffset uint16
DatabaseLength uint16
ClientID [6]byte
SSPIOffset uint16
SSPILength uint16
AtchDBFileOffset uint16
AtchDBFileLength uint16
ChangePasswordOffset uint16
ChangePasswordLength uint16
SSPILongLength uint32
}
// convert Go string to UTF-16 encoded []byte (littleEndian)
// done manually rather than using bytes and binary packages
// for performance reasons
func str2ucs2(s string) []byte {
res := utf16.Encode([]rune(s))
ucs2 := make([]byte, 2*len(res))
for i := 0; i < len(res); i++ {
ucs2[2*i] = byte(res[i])
ucs2[2*i+1] = byte(res[i] >> 8)
}
return ucs2
}
func ucs22str(s []byte) (string, error) {
if len(s)%2 != 0 {
return "", fmt.Errorf("Illegal UCS2 string length: %d", len(s))
}
buf := make([]uint16, len(s)/2)
for i := 0; i < len(s); i += 2 {
buf[i/2] = binary.LittleEndian.Uint16(s[i:])
}
return string(utf16.Decode(buf)), nil
}
func manglePassword(password string) []byte {
var ucs2password []byte = str2ucs2(password)
for i, ch := range ucs2password {
ucs2password[i] = ((ch<<4)&0xff | (ch >> 4)) ^ 0xA5
}
return ucs2password
}
// http://msdn.microsoft.com/en-us/library/dd304019.aspx
func sendLogin(w *tdsBuffer, login login) error {
w.BeginPacket(packLogin7, false)
hostname := str2ucs2(login.HostName)
username := str2ucs2(login.UserName)
password := manglePassword(login.Password)
appname := str2ucs2(login.AppName)
servername := str2ucs2(login.ServerName)
ctlintname := str2ucs2(login.CtlIntName)
language := str2ucs2(login.Language)
database := str2ucs2(login.Database)
atchdbfile := str2ucs2(login.AtchDBFile)
changepassword := str2ucs2(login.ChangePassword)
hdr := loginHeader{
TDSVersion: login.TDSVersion,
PacketSize: login.PacketSize,
ClientProgVer: login.ClientProgVer,
ClientPID: login.ClientPID,
ConnectionID: login.ConnectionID,
OptionFlags1: login.OptionFlags1,
OptionFlags2: login.OptionFlags2,
TypeFlags: login.TypeFlags,
OptionFlags3: login.OptionFlags3,
ClientTimeZone: login.ClientTimeZone,
ClientLCID: login.ClientLCID,
HostNameLength: uint16(utf8.RuneCountInString(login.HostName)),
UserNameLength: uint16(utf8.RuneCountInString(login.UserName)),
PasswordLength: uint16(utf8.RuneCountInString(login.Password)),
AppNameLength: uint16(utf8.RuneCountInString(login.AppName)),
ServerNameLength: uint16(utf8.RuneCountInString(login.ServerName)),
CtlIntNameLength: uint16(utf8.RuneCountInString(login.CtlIntName)),
LanguageLength: uint16(utf8.RuneCountInString(login.Language)),
DatabaseLength: uint16(utf8.RuneCountInString(login.Database)),
ClientID: login.ClientID,
SSPILength: uint16(len(login.SSPI)),
AtchDBFileLength: uint16(utf8.RuneCountInString(login.AtchDBFile)),
ChangePasswordLength: uint16(utf8.RuneCountInString(login.ChangePassword)),
}
offset := uint16(binary.Size(hdr))
hdr.HostNameOffset = offset
offset += uint16(len(hostname))
hdr.UserNameOffset = offset
offset += uint16(len(username))
hdr.PasswordOffset = offset
offset += uint16(len(password))
hdr.AppNameOffset = offset
offset += uint16(len(appname))
hdr.ServerNameOffset = offset
offset += uint16(len(servername))
hdr.CtlIntNameOffset = offset
offset += uint16(len(ctlintname))
hdr.LanguageOffset = offset
offset += uint16(len(language))
hdr.DatabaseOffset = offset
offset += uint16(len(database))
hdr.SSPIOffset = offset
offset += uint16(len(login.SSPI))
hdr.AtchDBFileOffset = offset
offset += uint16(len(atchdbfile))
hdr.ChangePasswordOffset = offset
offset += uint16(len(changepassword))
hdr.Length = uint32(offset)
var err error
err = binary.Write(w, binary.LittleEndian, &hdr)
if err != nil {
return err
}
_, err = w.Write(hostname)
if err != nil {
return err
}
_, err = w.Write(username)
if err != nil {
return err
}
_, err = w.Write(password)
if err != nil {
return err
}
_, err = w.Write(appname)
if err != nil {
return err
}
_, err = w.Write(servername)
if err != nil {
return err
}
_, err = w.Write(ctlintname)
if err != nil {
return err
}
_, err = w.Write(language)
if err != nil {
return err
}
_, err = w.Write(database)
if err != nil {
return err
}
_, err = w.Write(login.SSPI)
if err != nil {
return err
}
_, err = w.Write(atchdbfile)
if err != nil {
return err
}
_, err = w.Write(changepassword)
if err != nil {
return err
}
return w.FinishPacket()
}
func readUcs2(r io.Reader, numchars int) (res string, err error) {
buf := make([]byte, numchars*2)
_, err = io.ReadFull(r, buf)
if err != nil {
return "", err
}
return ucs22str(buf)
}
func readUsVarChar(r io.Reader) (res string, err error) {
numchars, err := readUshort(r)
if err != nil {
return
}
return readUcs2(r, int(numchars))
}
func writeUsVarChar(w io.Writer, s string) (err error) {
buf := str2ucs2(s)
var numchars int = len(buf) / 2
if numchars > 0xffff {
panic("invalid size for US_VARCHAR")
}
err = binary.Write(w, binary.LittleEndian, uint16(numchars))
if err != nil {
return
}
_, err = w.Write(buf)
return
}
func readBVarChar(r io.Reader) (res string, err error) {
numchars, err := readByte(r)
if err != nil {
return "", err
}
// A zero length could be returned, return an empty string
if numchars == 0 {
return "", nil
}
return readUcs2(r, int(numchars))
}
func writeBVarChar(w io.Writer, s string) (err error) {
buf := str2ucs2(s)
var numchars int = len(buf) / 2
if numchars > 0xff {
panic("invalid size for B_VARCHAR")
}
err = binary.Write(w, binary.LittleEndian, uint8(numchars))
if err != nil {
return
}
_, err = w.Write(buf)
return
}
func readBVarByte(r io.Reader) (res []byte, err error) {
length, err := readByte(r)
if err != nil {
return
}
res = make([]byte, length)
_, err = io.ReadFull(r, res)
return
}
func readUshort(r io.Reader) (res uint16, err error) {
err = binary.Read(r, binary.LittleEndian, &res)
return
}
func readByte(r io.Reader) (res byte, err error) {
var b [1]byte
_, err = r.Read(b[:])
res = b[0]
return
}
// Packet Data Stream Headers
// http://msdn.microsoft.com/en-us/library/dd304953.aspx
type headerStruct struct {
hdrtype uint16
data []byte
}
const (
dataStmHdrQueryNotif = 1 // query notifications
dataStmHdrTransDescr = 2 // MARS transaction descriptor (required)
dataStmHdrTraceActivity = 3
)
// Query Notifications Header
// http://msdn.microsoft.com/en-us/library/dd304949.aspx
type queryNotifHdr struct {
notifyId string
ssbDeployment string
notifyTimeout uint32
}
func (hdr queryNotifHdr) pack() (res []byte) {
notifyId := str2ucs2(hdr.notifyId)
ssbDeployment := str2ucs2(hdr.ssbDeployment)
res = make([]byte, 2+len(notifyId)+2+len(ssbDeployment)+4)
b := res
binary.LittleEndian.PutUint16(b, uint16(len(notifyId)))
b = b[2:]
copy(b, notifyId)
b = b[len(notifyId):]
binary.LittleEndian.PutUint16(b, uint16(len(ssbDeployment)))
b = b[2:]
copy(b, ssbDeployment)
b = b[len(ssbDeployment):]
binary.LittleEndian.PutUint32(b, hdr.notifyTimeout)
return res
}
// MARS Transaction Descriptor Header
// http://msdn.microsoft.com/en-us/library/dd340515.aspx
type transDescrHdr struct {
transDescr uint64 // transaction descriptor returned from ENVCHANGE
outstandingReqCnt uint32 // outstanding request count
}
func (hdr transDescrHdr) pack() (res []byte) {
res = make([]byte, 8+4)
binary.LittleEndian.PutUint64(res, hdr.transDescr)
binary.LittleEndian.PutUint32(res[8:], hdr.outstandingReqCnt)
return res
}
func writeAllHeaders(w io.Writer, headers []headerStruct) (err error) {
// Calculating total length.
var totallen uint32 = 4
for _, hdr := range headers {
totallen += 4 + 2 + uint32(len(hdr.data))
}
// writing
err = binary.Write(w, binary.LittleEndian, totallen)
if err != nil {
return err
}
for _, hdr := range headers {
var headerlen uint32 = 4 + 2 + uint32(len(hdr.data))
err = binary.Write(w, binary.LittleEndian, headerlen)
if err != nil {
return err
}
err = binary.Write(w, binary.LittleEndian, hdr.hdrtype)
if err != nil {
return err
}
_, err = w.Write(hdr.data)
if err != nil {
return err
}
}
return nil
}
func sendSqlBatch72(buf *tdsBuffer, sqltext string, headers []headerStruct, resetSession bool) (err error) {
buf.BeginPacket(packSQLBatch, resetSession)
if err = writeAllHeaders(buf, headers); err != nil {
return
}
_, err = buf.Write(str2ucs2(sqltext))
if err != nil {
return
}
return buf.FinishPacket()
}
// 2.2.1.7 Attention: https://msdn.microsoft.com/en-us/library/dd341449.aspx
// 4.19.2 Out-of-Band Attention Signal: https://msdn.microsoft.com/en-us/library/dd305167.aspx
func sendAttention(buf *tdsBuffer) error {
buf.BeginPacket(packAttention, false)
return buf.FinishPacket()
}
type auth interface {
InitialBytes() ([]byte, error)
NextBytes([]byte) ([]byte, error)
Free()
}
// SQL Server AlwaysOn Availability Group Listeners are bound by DNS to a
// list of IP addresses. So if there is more than one, try them all and
// use the first one that allows a connection.
func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn net.Conn, err error) {
var ips []net.IP
ips, err = net.LookupIP(p.host)
if err != nil {
ip := net.ParseIP(p.host)
if ip == nil {
return nil, err
}
ips = []net.IP{ip}
}
if len(ips) == 1 {
d := c.getDialer(&p)
addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(p.port)))
conn, err = d.DialContext(ctx, "tcp", addr)
} else {
//Try Dials in parallel to avoid waiting for timeouts.
connChan := make(chan net.Conn, len(ips))
errChan := make(chan error, len(ips))
portStr := strconv.Itoa(int(p.port))
for _, ip := range ips {
go func(ip net.IP) {
d := c.getDialer(&p)
addr := net.JoinHostPort(ip.String(), portStr)
conn, err := d.DialContext(ctx, "tcp", addr)
if err == nil {
connChan <- conn
} else {
errChan <- err
}
}(ip)
}
// Wait for either the *first* successful connection, or all the errors
wait_loop:
for i, _ := range ips {
select {
case conn = <-connChan:
// Got a connection to use, close any others
go func(n int) {
for i := 0; i < n; i++ {
select {
case conn := <-connChan:
conn.Close()
case <-errChan:
}
}
}(len(ips) - i - 1)
// Remove any earlier errors we may have collected
err = nil
break wait_loop
case err = <-errChan:
}
}
}
// Can't do the usual err != nil check, as it is possible to have gotten an error before a successful connection
if conn == nil {
f := "Unable to open tcp connection with host '%v:%v': %v"
return nil, fmt.Errorf(f, p.host, p.port, err.Error())
}
return conn, err
}
func connect(ctx context.Context, c *Connector, log optionalLogger, p connectParams) (res *tdsSession, err error) {
dialCtx := ctx
if p.dial_timeout > 0 {
var cancel func()
dialCtx, cancel = context.WithTimeout(ctx, p.dial_timeout)
defer cancel()
}
// if instance is specified use instance resolution service
if p.instance != "" {
p.instance = strings.ToUpper(p.instance)
d := c.getDialer(&p)
instances, err := getInstances(dialCtx, d, p.host)
if err != nil {
f := "Unable to get instances from Sql Server Browser on host %v: %v"
return nil, fmt.Errorf(f, p.host, err.Error())
}
strport, ok := instances[p.instance]["tcp"]
if !ok {
f := "No instance matching '%v' returned from host '%v'"
return nil, fmt.Errorf(f, p.instance, p.host)
}
p.port, err = strconv.ParseUint(strport, 0, 16)
if err != nil {
f := "Invalid tcp port returned from Sql Server Browser '%v': %v"
return nil, fmt.Errorf(f, strport, err.Error())
}
}
initiate_connection:
conn, err := dialConnection(dialCtx, c, p)
if err != nil {
return nil, err
}
toconn := newTimeoutConn(conn, p.conn_timeout)
outbuf := newTdsBuffer(p.packetSize, toconn)
sess := tdsSession{
buf: outbuf,
log: log,
logFlags: p.logFlags,
}
instance_buf := []byte(p.instance)
instance_buf = append(instance_buf, 0) // zero terminate instance name
var encrypt byte
if p.disableEncryption {
encrypt = encryptNotSup
} else if p.encrypt {
encrypt = encryptOn
} else {
encrypt = encryptOff
}
fields := map[uint8][]byte{
preloginVERSION: {0, 0, 0, 0, 0, 0},
preloginENCRYPTION: {encrypt},
preloginINSTOPT: instance_buf,
preloginTHREADID: {0, 0, 0, 0},
preloginMARS: {0}, // MARS disabled
}
err = writePrelogin(outbuf, fields)
if err != nil {
return nil, err
}
fields, err = readPrelogin(outbuf)
if err != nil {
return nil, err
}
encryptBytes, ok := fields[preloginENCRYPTION]
if !ok {
return nil, fmt.Errorf("Encrypt negotiation failed")
}
encrypt = encryptBytes[0]
if p.encrypt && (encrypt == encryptNotSup || encrypt == encryptOff) {
return nil, fmt.Errorf("Server does not support encryption")
}
if encrypt != encryptNotSup {
var config tls.Config
if p.certificate != "" {
pem, err := ioutil.ReadFile(p.certificate)
if err != nil {
return nil, fmt.Errorf("Cannot read certificate %q: %v", p.certificate, err)
}
certs := x509.NewCertPool()
certs.AppendCertsFromPEM(pem)
config.RootCAs = certs
}
if p.trustServerCertificate {
config.InsecureSkipVerify = true
}
config.ServerName = p.hostInCertificate
// fix for https://github.com/denisenkom/go-mssqldb/issues/166
// Go implementation of TLS payload size heuristic algorithm splits single TDS package to multiple TCP segments,
// while SQL Server seems to expect one TCP segment per encrypted TDS package.
// Setting DynamicRecordSizingDisabled to true disables that algorithm and uses 16384 bytes per TLS package
config.DynamicRecordSizingDisabled = true
// setting up connection handler which will allow wrapping of TLS handshake packets inside TDS stream
handshakeConn := tlsHandshakeConn{buf: outbuf}
passthrough := passthroughConn{c: &handshakeConn}
tlsConn := tls.Client(&passthrough, &config)
err = tlsConn.Handshake()
passthrough.c = toconn
outbuf.transport = tlsConn
if err != nil {
return nil, fmt.Errorf("TLS Handshake failed: %v", err)
}
if encrypt == encryptOff {
outbuf.afterFirst = func() {
outbuf.transport = toconn
}
}
}
login := login{
TDSVersion: verTDS74,
PacketSize: uint32(outbuf.PackageSize()),
Database: p.database,
OptionFlags2: fODBC, // to get unlimited TEXTSIZE
HostName: p.workstation,
ServerName: p.host,
AppName: p.appname,
TypeFlags: p.typeFlags,
}
auth, auth_ok := getAuth(p.user, p.password, p.serverSPN, p.workstation)
if auth_ok {
login.SSPI, err = auth.InitialBytes()
if err != nil {
return nil, err
}
login.OptionFlags2 |= fIntSecurity
defer auth.Free()
} else {
login.UserName = p.user
login.Password = p.password
}
err = sendLogin(outbuf, login)
if err != nil {
return nil, err
}
// processing login response
success := false
for {
tokchan := make(chan tokenStruct, 5)
go processResponse(context.Background(), &sess, tokchan, nil)
for tok := range tokchan {
switch token := tok.(type) {
case sspiMsg:
sspi_msg, err := auth.NextBytes(token)
if err != nil {
return nil, err
}
if sspi_msg != nil && len(sspi_msg) > 0 {
outbuf.BeginPacket(packSSPIMessage, false)
_, err = outbuf.Write(sspi_msg)
if err != nil {
return nil, err
}
err = outbuf.FinishPacket()
if err != nil {
return nil, err
}
sspi_msg = nil
}
case loginAckStruct:
success = true
sess.loginAck = token
case error:
return nil, fmt.Errorf("Login error: %s", token.Error())
case doneStruct:
if token.isError() {
return nil, fmt.Errorf("Login error: %s", token.getError())
}
goto loginEnd
}
}
}
loginEnd:
if !success {
return nil, fmt.Errorf("Login failed")
}
if sess.routedServer != "" {
toconn.Close()
p.host = sess.routedServer
p.port = uint64(sess.routedPort)
if !p.hostInCertificateProvided {
p.hostInCertificate = sess.routedServer
}
goto initiate_connection
}
return &sess, nil
}
-804
View File
@@ -1,804 +0,0 @@
package mssql
import (
"context"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"strconv"
"strings"
)
//go:generate stringer -type token
type token byte
// token ids
const (
tokenReturnStatus token = 121 // 0x79
tokenColMetadata token = 129 // 0x81
tokenOrder token = 169 // 0xA9
tokenError token = 170 // 0xAA
tokenInfo token = 171 // 0xAB
tokenReturnValue token = 0xAC
tokenLoginAck token = 173 // 0xad
tokenRow token = 209 // 0xd1
tokenNbcRow token = 210 // 0xd2
tokenEnvChange token = 227 // 0xE3
tokenSSPI token = 237 // 0xED
tokenDone token = 253 // 0xFD
tokenDoneProc token = 254
tokenDoneInProc token = 255
)
// done flags
// https://msdn.microsoft.com/en-us/library/dd340421.aspx
const (
doneFinal = 0
doneMore = 1
doneError = 2
doneInxact = 4
doneCount = 0x10
doneAttn = 0x20
doneSrvError = 0x100
)
// ENVCHANGE types
// http://msdn.microsoft.com/en-us/library/dd303449.aspx
const (
envTypDatabase = 1
envTypLanguage = 2
envTypCharset = 3
envTypPacketSize = 4
envSortId = 5
envSortFlags = 6
envSqlCollation = 7
envTypBeginTran = 8
envTypCommitTran = 9
envTypRollbackTran = 10
envEnlistDTC = 11
envDefectTran = 12
envDatabaseMirrorPartner = 13
envPromoteTran = 15
envTranMgrAddr = 16
envTranEnded = 17
envResetConnAck = 18
envStartedInstanceName = 19
envRouting = 20
)
// COLMETADATA flags
// https://msdn.microsoft.com/en-us/library/dd357363.aspx
const (
colFlagNullable = 1
// TODO implement more flags
)
// interface for all tokens
type tokenStruct interface{}
type orderStruct struct {
ColIds []uint16
}
type doneStruct struct {
Status uint16
CurCmd uint16
RowCount uint64
errors []Error
}
func (d doneStruct) isError() bool {
return d.Status&doneError != 0 || len(d.errors) > 0
}
func (d doneStruct) getError() Error {
if len(d.errors) > 0 {
return d.errors[len(d.errors)-1]
} else {
return Error{Message: "Request failed but didn't provide reason"}
}
}
type doneInProcStruct doneStruct
var doneFlags2str = map[uint16]string{
doneFinal: "final",
doneMore: "more",
doneError: "error",
doneInxact: "inxact",
doneCount: "count",
doneAttn: "attn",
doneSrvError: "srverror",
}
func doneFlags2Str(flags uint16) string {
strs := make([]string, 0, len(doneFlags2str))
for flag, tag := range doneFlags2str {
if flags&flag != 0 {
strs = append(strs, tag)
}
}
return strings.Join(strs, "|")
}
// ENVCHANGE stream
// http://msdn.microsoft.com/en-us/library/dd303449.aspx
func processEnvChg(sess *tdsSession) {
size := sess.buf.uint16()
r := &io.LimitedReader{R: sess.buf, N: int64(size)}
for {
var err error
var envtype uint8
err = binary.Read(r, binary.LittleEndian, &envtype)
if err == io.EOF {
return
}
if err != nil {
badStreamPanic(err)
}
switch envtype {
case envTypDatabase:
sess.database, err = readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
_, err = readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
case envTypLanguage:
// currently ignored
// new value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envTypCharset:
// currently ignored
// new value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envTypPacketSize:
packetsize, err := readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
_, err = readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
packetsizei, err := strconv.Atoi(packetsize)
if err != nil {
badStreamPanicf("Invalid Packet size value returned from server (%s): %s", packetsize, err.Error())
}
sess.buf.ResizeBuffer(packetsizei)
case envSortId:
// currently ignored
// new value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envSortFlags:
// currently ignored
// new value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envSqlCollation:
// currently ignored
var collationSize uint8
err = binary.Read(r, binary.LittleEndian, &collationSize)
if err != nil {
badStreamPanic(err)
}
// SQL Collation data should contain 5 bytes in length
if collationSize != 5 {
badStreamPanicf("Invalid SQL Collation size value returned from server: %d", collationSize)
}
// 4 bytes, contains: LCID ColFlags Version
var info uint32
err = binary.Read(r, binary.LittleEndian, &info)
if err != nil {
badStreamPanic(err)
}
// 1 byte, contains: sortID
var sortID uint8
err = binary.Read(r, binary.LittleEndian, &sortID)
if err != nil {
badStreamPanic(err)
}
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envTypBeginTran:
tranid, err := readBVarByte(r)
if len(tranid) != 8 {
badStreamPanicf("invalid size of transaction identifier: %d", len(tranid))
}
sess.tranid = binary.LittleEndian.Uint64(tranid)
if err != nil {
badStreamPanic(err)
}
if sess.logFlags&logTransaction != 0 {
sess.log.Printf("BEGIN TRANSACTION %x\n", sess.tranid)
}
_, err = readBVarByte(r)
if err != nil {
badStreamPanic(err)
}
case envTypCommitTran, envTypRollbackTran:
_, err = readBVarByte(r)
if err != nil {
badStreamPanic(err)
}
_, err = readBVarByte(r)
if err != nil {
badStreamPanic(err)
}
if sess.logFlags&logTransaction != 0 {
if envtype == envTypCommitTran {
sess.log.Printf("COMMIT TRANSACTION %x\n", sess.tranid)
} else {
sess.log.Printf("ROLLBACK TRANSACTION %x\n", sess.tranid)
}
}
sess.tranid = 0
case envEnlistDTC:
// currently ignored
// new value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envDefectTran:
// currently ignored
// new value
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envDatabaseMirrorPartner:
sess.partner, err = readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
_, err = readBVarChar(r)
if err != nil {
badStreamPanic(err)
}
case envPromoteTran:
// currently ignored
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// dtc token
// spec says it should be L_VARBYTE, so this code might be wrong
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envTranMgrAddr:
// currently ignored
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// XACT_MANAGER_ADDRESS = B_VARBYTE
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envTranEnded:
// currently ignored
// old value, B_VARBYTE
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envResetConnAck:
// currently ignored
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envStartedInstanceName:
// currently ignored
// old value, should be 0
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
// instance name
if _, err = readBVarChar(r); err != nil {
badStreamPanic(err)
}
case envRouting:
// RoutingData message is:
// ValueLength USHORT
// Protocol (TCP = 0) BYTE
// ProtocolProperty (new port) USHORT
// AlternateServer US_VARCHAR
_, err := readUshort(r)
if err != nil {
badStreamPanic(err)
}
protocol, err := readByte(r)
if err != nil || protocol != 0 {
badStreamPanic(err)
}
newPort, err := readUshort(r)
if err != nil {
badStreamPanic(err)
}
newServer, err := readUsVarChar(r)
if err != nil {
badStreamPanic(err)
}
// consume the OLDVALUE = %x00 %x00
_, err = readUshort(r)
if err != nil {
badStreamPanic(err)
}
sess.routedServer = newServer
sess.routedPort = newPort
default:
// ignore rest of records because we don't know how to skip those
sess.log.Printf("WARN: Unknown ENVCHANGE record detected with type id = %d\n", envtype)
break
}
}
}
// http://msdn.microsoft.com/en-us/library/dd358180.aspx
func parseReturnStatus(r *tdsBuffer) ReturnStatus {
return ReturnStatus(r.int32())
}
func parseOrder(r *tdsBuffer) (res orderStruct) {
len := int(r.uint16())
res.ColIds = make([]uint16, len/2)
for i := 0; i < len/2; i++ {
res.ColIds[i] = r.uint16()
}
return res
}
// https://msdn.microsoft.com/en-us/library/dd340421.aspx
func parseDone(r *tdsBuffer) (res doneStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = r.uint64()
return res
}
// https://msdn.microsoft.com/en-us/library/dd340553.aspx
func parseDoneInProc(r *tdsBuffer) (res doneInProcStruct) {
res.Status = r.uint16()
res.CurCmd = r.uint16()
res.RowCount = r.uint64()
return res
}
type sspiMsg []byte
func parseSSPIMsg(r *tdsBuffer) sspiMsg {
size := r.uint16()
buf := make([]byte, size)
r.ReadFull(buf)
return sspiMsg(buf)
}
type loginAckStruct struct {
Interface uint8
TDSVersion uint32
ProgName string
ProgVer uint32
}
func parseLoginAck(r *tdsBuffer) loginAckStruct {
size := r.uint16()
buf := make([]byte, size)
r.ReadFull(buf)
var res loginAckStruct
res.Interface = buf[0]
res.TDSVersion = binary.BigEndian.Uint32(buf[1:])
prognamelen := buf[1+4]
var err error
if res.ProgName, err = ucs22str(buf[1+4+1 : 1+4+1+prognamelen*2]); err != nil {
badStreamPanic(err)
}
res.ProgVer = binary.BigEndian.Uint32(buf[size-4:])
return res
}
// http://msdn.microsoft.com/en-us/library/dd357363.aspx
func parseColMetadata72(r *tdsBuffer) (columns []columnStruct) {
count := r.uint16()
if count == 0xffff {
// no metadata is sent
return nil
}
columns = make([]columnStruct, count)
for i := range columns {
column := &columns[i]
column.UserType = r.uint32()
column.Flags = r.uint16()
// parsing TYPE_INFO structure
column.ti = readTypeInfo(r)
column.ColName = r.BVarChar()
}
return columns
}
// http://msdn.microsoft.com/en-us/library/dd357254.aspx
func parseRow(r *tdsBuffer, columns []columnStruct, row []interface{}) {
for i, column := range columns {
row[i] = column.ti.Reader(&column.ti, r)
}
}
// http://msdn.microsoft.com/en-us/library/dd304783.aspx
func parseNbcRow(r *tdsBuffer, columns []columnStruct, row []interface{}) {
bitlen := (len(columns) + 7) / 8
pres := make([]byte, bitlen)
r.ReadFull(pres)
for i, col := range columns {
if pres[i/8]&(1<<(uint(i)%8)) != 0 {
row[i] = nil
continue
}
row[i] = col.ti.Reader(&col.ti, r)
}
}
// http://msdn.microsoft.com/en-us/library/dd304156.aspx
func parseError72(r *tdsBuffer) (res Error) {
length := r.uint16()
_ = length // ignore length
res.Number = r.int32()
res.State = r.byte()
res.Class = r.byte()
res.Message = r.UsVarChar()
res.ServerName = r.BVarChar()
res.ProcName = r.BVarChar()
res.LineNo = r.int32()
return
}
// http://msdn.microsoft.com/en-us/library/dd304156.aspx
func parseInfo(r *tdsBuffer) (res Error) {
length := r.uint16()
_ = length // ignore length
res.Number = r.int32()
res.State = r.byte()
res.Class = r.byte()
res.Message = r.UsVarChar()
res.ServerName = r.BVarChar()
res.ProcName = r.BVarChar()
res.LineNo = r.int32()
return
}
// https://msdn.microsoft.com/en-us/library/dd303881.aspx
func parseReturnValue(r *tdsBuffer) (nv namedValue) {
/*
ParamOrdinal
ParamName
Status
UserType
Flags
TypeInfo
CryptoMetadata
Value
*/
r.uint16()
nv.Name = r.BVarChar()
r.byte()
r.uint32() // UserType (uint16 prior to 7.2)
r.uint16()
ti := readTypeInfo(r)
nv.Value = ti.Reader(&ti, r)
return
}
func processSingleResponse(sess *tdsSession, ch chan tokenStruct, outs map[string]interface{}) {
defer func() {
if err := recover(); err != nil {
if sess.logFlags&logErrors != 0 {
sess.log.Printf("ERROR: Intercepted panic %v", err)
}
ch <- err
}
close(ch)
}()
packet_type, err := sess.buf.BeginRead()
if err != nil {
if sess.logFlags&logErrors != 0 {
sess.log.Printf("ERROR: BeginRead failed %v", err)
}
ch <- err
return
}
if packet_type != packReply {
badStreamPanic(fmt.Errorf("unexpected packet type in reply: got %v, expected %v", packet_type, packReply))
}
var columns []columnStruct
errs := make([]Error, 0, 5)
for {
token := token(sess.buf.byte())
if sess.logFlags&logDebug != 0 {
sess.log.Printf("got token %v", token)
}
switch token {
case tokenSSPI:
ch <- parseSSPIMsg(sess.buf)
return
case tokenReturnStatus:
returnStatus := parseReturnStatus(sess.buf)
ch <- returnStatus
case tokenLoginAck:
loginAck := parseLoginAck(sess.buf)
ch <- loginAck
case tokenOrder:
order := parseOrder(sess.buf)
ch <- order
case tokenDoneInProc:
done := parseDoneInProc(sess.buf)
if sess.logFlags&logRows != 0 && done.Status&doneCount != 0 {
sess.log.Printf("(%d row(s) affected)\n", done.RowCount)
}
ch <- done
case tokenDone, tokenDoneProc:
done := parseDone(sess.buf)
done.errors = errs
if sess.logFlags&logDebug != 0 {
sess.log.Printf("got DONE or DONEPROC status=%d", done.Status)
}
if done.Status&doneSrvError != 0 {
ch <- errors.New("SQL Server had internal error")
return
}
if sess.logFlags&logRows != 0 && done.Status&doneCount != 0 {
sess.log.Printf("(%d row(s) affected)\n", done.RowCount)
}
ch <- done
if done.Status&doneMore == 0 {
return
}
case tokenColMetadata:
columns = parseColMetadata72(sess.buf)
ch <- columns
case tokenRow:
row := make([]interface{}, len(columns))
parseRow(sess.buf, columns, row)
ch <- row
case tokenNbcRow:
row := make([]interface{}, len(columns))
parseNbcRow(sess.buf, columns, row)
ch <- row
case tokenEnvChange:
processEnvChg(sess)
case tokenError:
err := parseError72(sess.buf)
if sess.logFlags&logDebug != 0 {
sess.log.Printf("got ERROR %d %s", err.Number, err.Message)
}
errs = append(errs, err)
if sess.logFlags&logErrors != 0 {
sess.log.Println(err.Message)
}
case tokenInfo:
info := parseInfo(sess.buf)
if sess.logFlags&logDebug != 0 {
sess.log.Printf("got INFO %d %s", info.Number, info.Message)
}
if sess.logFlags&logMessages != 0 {
sess.log.Println(info.Message)
}
case tokenReturnValue:
nv := parseReturnValue(sess.buf)
if len(nv.Name) > 0 {
name := nv.Name[1:] // Remove the leading "@".
if ov, has := outs[name]; has {
err = scanIntoOut(name, nv.Value, ov)
if err != nil {
fmt.Println("scan error", err)
ch <- err
}
}
}
default:
badStreamPanic(fmt.Errorf("unknown token type returned: %v", token))
}
}
}
type parseRespIter byte
const (
parseRespIterContinue parseRespIter = iota // Continue parsing current token.
parseRespIterNext // Fetch the next token.
parseRespIterDone // Done with parsing the response.
)
type parseRespState byte
const (
parseRespStateNormal parseRespState = iota // Normal response state.
parseRespStateCancel // Query is canceled, wait for server to confirm.
parseRespStateClosing // Waiting for tokens to come through.
)
type parseResp struct {
sess *tdsSession
ctxDone <-chan struct{}
state parseRespState
cancelError error
}
func (ts *parseResp) sendAttention(ch chan tokenStruct) parseRespIter {
if err := sendAttention(ts.sess.buf); err != nil {
ts.dlogf("failed to send attention signal %v", err)
ch <- err
return parseRespIterDone
}
ts.state = parseRespStateCancel
return parseRespIterContinue
}
func (ts *parseResp) dlog(msg string) {
if ts.sess.logFlags&logDebug != 0 {
ts.sess.log.Println(msg)
}
}
func (ts *parseResp) dlogf(f string, v ...interface{}) {
if ts.sess.logFlags&logDebug != 0 {
ts.sess.log.Printf(f, v...)
}
}
func (ts *parseResp) iter(ctx context.Context, ch chan tokenStruct, tokChan chan tokenStruct) parseRespIter {
switch ts.state {
default:
panic("unknown state")
case parseRespStateNormal:
select {
case tok, ok := <-tokChan:
if !ok {
ts.dlog("response finished")
return parseRespIterDone
}
if err, ok := tok.(net.Error); ok && err.Timeout() {
ts.cancelError = err
ts.dlog("got timeout error, sending attention signal to server")
return ts.sendAttention(ch)
}
// Pass the token along.
ch <- tok
return parseRespIterContinue
case <-ts.ctxDone:
ts.ctxDone = nil
ts.dlog("got cancel message, sending attention signal to server")
return ts.sendAttention(ch)
}
case parseRespStateCancel: // Read all responses until a DONE or error is received.Auth
select {
case tok, ok := <-tokChan:
if !ok {
ts.dlog("response finished but waiting for attention ack")
return parseRespIterNext
}
switch tok := tok.(type) {
default:
// Ignore all other tokens while waiting.
// The TDS spec says other tokens may arrive after an attention
// signal is sent. Ignore these tokens and continue looking for
// a DONE with attention confirm mark.
case doneStruct:
if tok.Status&doneAttn != 0 {
ts.dlog("got cancellation confirmation from server")
if ts.cancelError != nil {
ch <- ts.cancelError
ts.cancelError = nil
} else {
ch <- ctx.Err()
}
return parseRespIterDone
}
// If an error happens during cancel, pass it along and just stop.
// We are uncertain to receive more tokens.
case error:
ch <- tok
ts.state = parseRespStateClosing
}
return parseRespIterContinue
case <-ts.ctxDone:
ts.ctxDone = nil
ts.state = parseRespStateClosing
return parseRespIterContinue
}
case parseRespStateClosing: // Wait for current token chan to close.
if _, ok := <-tokChan; !ok {
ts.dlog("response finished")
return parseRespIterDone
}
return parseRespIterContinue
}
}
func processResponse(ctx context.Context, sess *tdsSession, ch chan tokenStruct, outs map[string]interface{}) {
ts := &parseResp{
sess: sess,
ctxDone: ctx.Done(),
}
defer func() {
// Ensure any remaining error is piped through
// or the query may look like it executed when it actually failed.
if ts.cancelError != nil {
ch <- ts.cancelError
ts.cancelError = nil
}
close(ch)
}()
// Loop over multiple responses.
for {
ts.dlog("initiating response reading")
tokChan := make(chan tokenStruct)
go processSingleResponse(sess, tokChan, outs)
// Loop over multiple tokens in response.
tokensLoop:
for {
switch ts.iter(ctx, ch, tokChan) {
case parseRespIterContinue:
// Nothing, continue to next token.
case parseRespIterNext:
break tokensLoop
case parseRespIterDone:
return
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More