connect: intermediate CA certs generated with the vault provider lack URI SANs (#6491)

This only affects vault versions >=1.1.1 because the prior code
accidentally relied upon a bug that was fixed in
https://github.com/hashicorp/vault/pull/6505

The existing tests should have caught this, but they were using a
vendored copy of vault version 0.10.3. This fixes the tests by running
an actual copy of vault instead of an in-process copy. This has the
added benefit of changing the dependency on vault to just vault/api.

Also update VaultProvider to use similar SetIntermediate validation code
as the ConsulProvider implementation.
This commit is contained in:
R.B. Boyer 2019-09-23 12:04:40 -05:00 committed by GitHub
parent 708b2a917c
commit 796de297c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
818 changed files with 9719 additions and 218595 deletions

View File

@ -22,6 +22,7 @@ references:
GIT_COMMITTER_NAME: circleci-consul
S3_ARTIFACT_BUCKET: consul-dev-artifacts
BASH_ENV: .circleci/bash_env.sh
VAULT_BINARY_VERSION: 1.2.2
jobs:
# lint consul tests
@ -101,6 +102,10 @@ jobs:
- run: mkdir -p $TEST_RESULTS_DIR
- run: sudo apt-get update && sudo apt-get install -y rsyslog
- run: sudo service rsyslog start
- run: |
wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/${VAULT_BINARY_VERSION}/vault_${VAULT_BINARY_VERSION}_linux_amd64.zip
sudo unzip -d /usr/local/bin /tmp/vault.zip
rm -rf /tmp/vault*
- run: |
PACKAGE_NAMES=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname)
gotestsum --format=short-verbose --junitfile $TEST_RESULTS_DIR/gotestsum-report.xml -- -tags=$GOTAGS -p 3 $PACKAGE_NAMES

View File

@ -0,0 +1,65 @@
package ca
import (
"bytes"
"crypto/x509"
"fmt"
"github.com/hashicorp/consul/agent/connect"
)
func validateSetIntermediate(
intermediatePEM, rootPEM string,
currentPrivateKey string, // optional
spiffeID *connect.SpiffeIDSigning,
) error {
// Get the key from the incoming intermediate cert so we can compare it
// to the currently stored key.
intermediate, err := connect.ParseCert(intermediatePEM)
if err != nil {
return fmt.Errorf("error parsing intermediate PEM: %v", err)
}
if currentPrivateKey != "" {
privKey, err := connect.ParseSigner(currentPrivateKey)
if err != nil {
return err
}
// Compare the two keys to make sure they match.
b1, err := x509.MarshalPKIXPublicKey(intermediate.PublicKey)
if err != nil {
return err
}
b2, err := x509.MarshalPKIXPublicKey(privKey.Public())
if err != nil {
return err
}
if !bytes.Equal(b1, b2) {
return fmt.Errorf("intermediate cert is for a different private key")
}
}
// Validate the remaining fields and make sure the intermediate validates against
// the given root cert.
if !intermediate.IsCA {
return fmt.Errorf("intermediate is not a CA certificate")
}
if uriCount := len(intermediate.URIs); uriCount != 1 {
return fmt.Errorf("incoming intermediate cert has unexpected number of URIs: %d", uriCount)
}
if got, want := intermediate.URIs[0].String(), spiffeID.URI().String(); got != want {
return fmt.Errorf("incoming cert URI %q does not match current URI: %q", got, want)
}
pool := x509.NewCertPool()
pool.AppendCertsFromPEM([]byte(rootPEM))
_, err = intermediate.Verify(x509.VerifyOptions{
Roots: pool,
})
if err != nil {
return fmt.Errorf("could not verify intermediate cert against root: %v", err)
}
return nil
}

View File

@ -220,51 +220,15 @@ func (c *ConsulProvider) SetIntermediate(intermediatePEM, rootPEM string) error
return fmt.Errorf("cannot set an intermediate using another root in the primary datacenter")
}
// Get the key from the incoming intermediate cert so we can compare it
// to the currently stored key.
intermediate, err := connect.ParseCert(intermediatePEM)
if err != nil {
return fmt.Errorf("error parsing intermediate PEM: %v", err)
}
privKey, err := connect.ParseSigner(providerState.PrivateKey)
err = validateSetIntermediate(
intermediatePEM, rootPEM,
providerState.PrivateKey,
c.spiffeID,
)
if err != nil {
return err
}
// Compare the two keys to make sure they match.
b1, err := x509.MarshalPKIXPublicKey(intermediate.PublicKey)
if err != nil {
return err
}
b2, err := x509.MarshalPKIXPublicKey(privKey.Public())
if err != nil {
return err
}
if !bytes.Equal(b1, b2) {
return fmt.Errorf("intermediate cert is for a different private key")
}
// Validate the remaining fields and make sure the intermediate validates against
// the given root cert.
if !intermediate.IsCA {
return fmt.Errorf("intermediate is not a CA certificate")
}
if uriCount := len(intermediate.URIs); uriCount != 1 {
return fmt.Errorf("incoming intermediate cert has unexpected number of URIs: %d", uriCount)
}
if got, want := intermediate.URIs[0].String(), c.spiffeID.URI().String(); got != want {
return fmt.Errorf("incoming cert URI %q does not match current URI: %q", got, want)
}
pool := x509.NewCertPool()
pool.AppendCertsFromPEM([]byte(rootPEM))
_, err = intermediate.Verify(x509.VerifyOptions{
Roots: pool,
})
if err != nil {
return fmt.Errorf("could not verify intermediate cert against root: %v", err)
}
// Update the state
newState := *providerState
newState.IntermediateCert = intermediatePEM

View File

@ -26,6 +26,7 @@ type VaultProvider struct {
client *vaultapi.Client
isRoot bool
clusterId string
spiffeID *connect.SpiffeIDSigning
}
func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig {
@ -63,6 +64,7 @@ func (v *VaultProvider) Configure(clusterId string, isRoot bool, rawConfig map[s
v.client = client
v.isRoot = isRoot
v.clusterId = clusterId
v.spiffeID = connect.SpiffeIDSigningForCluster(&structs.CAConfiguration{ClusterID: clusterId})
return nil
}
@ -96,14 +98,13 @@ func (v *VaultProvider) GenerateRoot() error {
fallthrough
case ErrBackendNotInitialized:
spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"}
uuid, err := uuid.GenerateUUID()
if err != nil {
return err
}
_, err = v.client.Logical().Write(v.config.RootPKIPath+"root/generate/internal", map[string]interface{}{
"common_name": fmt.Sprintf("Vault CA Root Authority %s", uuid),
"uri_sans": spiffeID.URI().String(),
"uri_sans": v.spiffeID.URI().String(),
"key_type": v.config.PrivateKeyType,
"key_bits": v.config.PrivateKeyBits,
})
@ -158,7 +159,6 @@ func (v *VaultProvider) generateIntermediateCSR() (string, error) {
if err != nil {
return "", err
}
spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"}
if role == nil {
_, err := v.client.Logical().Write(rolePath, map[string]interface{}{
"allow_any_name": true,
@ -178,7 +178,7 @@ func (v *VaultProvider) generateIntermediateCSR() (string, error) {
"common_name": "Vault CA Intermediate Authority",
"key_type": v.config.PrivateKeyType,
"key_bits": v.config.PrivateKeyBits,
"uri_sans": spiffeID.URI().String(),
"uri_sans": v.spiffeID.URI().String(),
})
if err != nil {
return "", err
@ -201,7 +201,16 @@ func (v *VaultProvider) SetIntermediate(intermediatePEM, rootPEM string) error {
return fmt.Errorf("cannot set an intermediate using another root in the primary datacenter")
}
_, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"intermediate/set-signed", map[string]interface{}{
err := validateSetIntermediate(
intermediatePEM, rootPEM,
"", // we don't have access to the private key directly
v.spiffeID,
)
if err != nil {
return err
}
_, err = v.client.Logical().Write(v.config.IntermediatePKIPath+"intermediate/set-signed", map[string]interface{}{
"certificate": fmt.Sprintf("%s\n%s", intermediatePEM, rootPEM),
})
if err != nil {
@ -258,6 +267,7 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
// Sign the CSR with the root backend.
intermediate, err := v.client.Logical().Write(v.config.RootPKIPath+"root/sign-intermediate", map[string]interface{}{
"csr": csr,
"use_csr_values": true,
"format": "pem_bundle",
})
if err != nil {
@ -323,6 +333,7 @@ func (v *VaultProvider) SignIntermediate(csr *x509.CertificateRequest) (string,
// Sign the CSR with the root backend.
data, err := v.client.Logical().Write(v.config.RootPKIPath+"root/sign-intermediate", map[string]interface{}{
"csr": pemBuf.String(),
"use_csr_values": true,
"format": "pem_bundle",
"max_path_length": 0,
})

View File

@ -3,61 +3,20 @@ package ca
import (
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"sync"
"testing"
"time"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/sdk/freeport"
"github.com/hashicorp/consul/sdk/testutil/retry"
vaultapi "github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/logical/pki"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
"github.com/stretchr/testify/require"
)
var vaultLock sync.Mutex
func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener) {
return testVaultClusterWithConfig(t, true, nil)
}
func testVaultClusterWithConfig(t *testing.T, isRoot bool, rawConf map[string]interface{}) (*VaultProvider, *vault.Core, net.Listener) {
vaultLock.Lock()
defer vaultLock.Unlock()
if err := vault.AddTestLogicalBackend("pki", pki.Factory); err != nil {
t.Fatal(err)
}
core, _, token := vault.TestCoreUnsealedRaw(t)
ln, addr := vaulthttp.TestServer(t, core)
conf := map[string]interface{}{
"Address": addr,
"Token": token,
"RootPKIPath": "pki-root/",
"IntermediatePKIPath": "pki-intermediate/",
// Tests duration parsing after msgpack type mangling during raft apply.
"LeafCertTTL": []uint8("72h"),
}
for k, v := range rawConf {
conf[k] = v
}
require := require.New(t)
provider := &VaultProvider{}
require.NoError(provider.Configure("asdf", isRoot, conf))
if isRoot {
require.NoError(provider.GenerateRoot())
_, err := provider.GenerateIntermediate()
require.NoError(err)
}
return provider, core, ln
}
func TestVaultCAProvider_VaultTLSConfig(t *testing.T) {
config := &structs.VaultCAProviderConfig{
CAFile: "/capath/ca.pem",
@ -80,15 +39,15 @@ func TestVaultCAProvider_VaultTLSConfig(t *testing.T) {
func TestVaultCAProvider_Bootstrap(t *testing.T) {
t.Parallel()
if skipIfVaultNotPresent(t) {
return
}
provider, testVault := testVaultProvider(t)
defer testVault.Stop()
client := testVault.client
require := require.New(t)
provider, core, listener := testVaultCluster(t)
defer core.Shutdown()
defer listener.Close()
client, err := vaultapi.NewClient(&vaultapi.Config{
Address: "http://" + listener.Addr().String(),
})
require.NoError(err)
client.SetToken(provider.config.Token)
cases := []struct {
certFunc func() (string, error)
@ -127,17 +86,15 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) {
func TestVaultCAProvider_SignLeaf(t *testing.T) {
t.Parallel()
if skipIfVaultNotPresent(t) {
return
}
require := require.New(t)
provider, core, listener := testVaultClusterWithConfig(t, true, map[string]interface{}{
provider, testVault := testVaultProviderWithConfig(t, true, map[string]interface{}{
"LeafCertTTL": "1h",
})
defer core.Shutdown()
defer listener.Close()
client, err := vaultapi.NewClient(&vaultapi.Config{
Address: "http://" + listener.Addr().String(),
})
require.NoError(err)
client.SetToken(provider.config.Token)
defer testVault.Stop()
spiffeService := &connect.SpiffeIDService{
Host: "node1",
@ -194,13 +151,15 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
func TestVaultCAProvider_CrossSignCA(t *testing.T) {
t.Parallel()
provider1, core1, listener1 := testVaultCluster(t)
defer core1.Shutdown()
defer listener1.Close()
if skipIfVaultNotPresent(t) {
return
}
provider2, core2, listener2 := testVaultCluster(t)
defer core2.Shutdown()
defer listener2.Close()
provider1, testVault1 := testVaultProvider(t)
defer testVault1.Stop()
provider2, testVault2 := testVaultProvider(t)
defer testVault2.Stop()
testCrossSignProviders(t, provider1, provider2)
}
@ -208,13 +167,15 @@ func TestVaultCAProvider_CrossSignCA(t *testing.T) {
func TestVaultProvider_SignIntermediate(t *testing.T) {
t.Parallel()
provider1, core1, listener1 := testVaultCluster(t)
defer core1.Shutdown()
defer listener1.Close()
if skipIfVaultNotPresent(t) {
return
}
provider2, core2, listener2 := testVaultClusterWithConfig(t, false, nil)
defer core2.Shutdown()
defer listener2.Close()
provider1, testVault1 := testVaultProvider(t)
defer testVault1.Stop()
provider2, testVault2 := testVaultProviderWithConfig(t, false, nil)
defer testVault2.Stop()
testSignIntermediateCrossDC(t, provider1, provider2)
}
@ -222,34 +183,210 @@ func TestVaultProvider_SignIntermediate(t *testing.T) {
func TestVaultProvider_SignIntermediateConsul(t *testing.T) {
t.Parallel()
require := require.New(t)
if skipIfVaultNotPresent(t) {
return
}
// primary = Vault, secondary = Consul
{
provider1, core, listener := testVaultCluster(t)
defer core.Shutdown()
defer listener.Close()
t.Run("pri=vault,sec=consul", func(t *testing.T) {
provider1, testVault1 := testVaultProviderWithConfig(t, true, nil)
defer testVault1.Stop()
conf := testConsulCAConfig()
delegate := newMockDelegate(t, conf)
provider2 := &ConsulProvider{Delegate: delegate}
require.NoError(provider2.Configure(conf.ClusterID, false, conf.Config))
require.NoError(t, provider2.Configure(conf.ClusterID, false, conf.Config))
testSignIntermediateCrossDC(t, provider1, provider2)
}
})
// primary = Consul, secondary = Vault
{
t.Run("pri=consul,sec=vault", func(t *testing.T) {
conf := testConsulCAConfig()
delegate := newMockDelegate(t, conf)
provider1 := &ConsulProvider{Delegate: delegate}
require.NoError(provider1.Configure(conf.ClusterID, true, conf.Config))
require.NoError(provider1.GenerateRoot())
require.NoError(t, provider1.Configure(conf.ClusterID, true, conf.Config))
require.NoError(t, provider1.GenerateRoot())
provider2, core, listener := testVaultClusterWithConfig(t, false, nil)
defer core.Shutdown()
defer listener.Close()
provider2, testVault2 := testVaultProviderWithConfig(t, false, nil)
defer testVault2.Stop()
testSignIntermediateCrossDC(t, provider1, provider2)
})
}
func testVaultProvider(t *testing.T) (*VaultProvider, *testVaultServer) {
return testVaultProviderWithConfig(t, true, nil)
}
func testVaultProviderWithConfig(t *testing.T, isRoot bool, rawConf map[string]interface{}) (*VaultProvider, *testVaultServer) {
testVault, err := runTestVault()
if err != nil {
t.Fatalf("err: %v", err)
}
testVault.WaitUntilReady(t)
conf := map[string]interface{}{
"Address": testVault.addr,
"Token": testVault.rootToken,
"RootPKIPath": "pki-root/",
"IntermediatePKIPath": "pki-intermediate/",
// Tests duration parsing after msgpack type mangling during raft apply.
"LeafCertTTL": []uint8("72h"),
}
for k, v := range rawConf {
conf[k] = v
}
provider := &VaultProvider{}
if err := provider.Configure("asdf", isRoot, conf); err != nil {
testVault.Stop()
t.Fatalf("err: %v", err)
}
if isRoot {
if err = provider.GenerateRoot(); err != nil {
testVault.Stop()
t.Fatalf("err: %v", err)
}
if _, err := provider.GenerateIntermediate(); err != nil {
testVault.Stop()
t.Fatalf("err: %v", err)
}
}
return provider, testVault
}
var printedVaultVersion sync.Once
// skipIfVaultNotPresent skips the test and returns true if vault is not found
func skipIfVaultNotPresent(t *testing.T) bool {
vaultBinaryName := os.Getenv("VAULT_BINARY_NAME")
if vaultBinaryName == "" {
vaultBinaryName = "vault"
}
path, err := exec.LookPath(vaultBinaryName)
if err != nil || path == "" {
t.Skipf("%q not found on $PATH - download and install to run this test", vaultBinaryName)
return true
}
return false
}
func runTestVault() (*testVaultServer, error) {
vaultBinaryName := os.Getenv("VAULT_BINARY_NAME")
if vaultBinaryName == "" {
vaultBinaryName = "vault"
}
path, err := exec.LookPath(vaultBinaryName)
if err != nil || path == "" {
return nil, fmt.Errorf("%q not found on $PATH", vaultBinaryName)
}
ports := freeport.MustTake(2)
returnPortsFn := func() {
freeport.Return(ports)
}
var (
clientAddr = fmt.Sprintf("127.0.0.1:%d", ports[0])
clusterAddr = fmt.Sprintf("127.0.0.1:%d", ports[1])
)
const token = "root"
client, err := vaultapi.NewClient(&vaultapi.Config{
Address: "http://" + clientAddr,
})
if err != nil {
returnPortsFn()
return nil, err
}
client.SetToken(token)
args := []string{
"server",
"-dev",
"-dev-root-token-id",
token,
"-dev-listen-address",
clientAddr,
"-address",
clusterAddr,
}
cmd := exec.Command(vaultBinaryName, args...)
cmd.Stdout = ioutil.Discard
cmd.Stderr = ioutil.Discard
if err := cmd.Start(); err != nil {
returnPortsFn()
return nil, err
}
return &testVaultServer{
rootToken: token,
addr: "http://" + clientAddr,
cmd: cmd,
client: client,
returnPortsFn: returnPortsFn,
}, nil
}
type testVaultServer struct {
rootToken string
addr string
cmd *exec.Cmd
client *vaultapi.Client
// returnPortsFn will put the ports claimed for the test back into the
returnPortsFn func()
}
func (v *testVaultServer) WaitUntilReady(t *testing.T) {
var version string
retry.Run(t, func(r *retry.R) {
resp, err := v.client.Sys().Health()
if err != nil {
r.Fatalf("err: %v", err)
}
if !resp.Initialized {
r.Fatalf("vault server is not initialized")
}
if resp.Sealed {
r.Fatalf("vault server is sealed")
}
version = resp.Version
})
printedVaultVersion.Do(func() {
fmt.Fprintf(os.Stderr, "[INFO] agent/connect/ca: testing with vault server version: %s\n", version)
})
}
func (v *testVaultServer) Stop() error {
// There was no process
if v.cmd == nil {
return nil
}
if v.cmd.Process != nil {
if err := v.cmd.Process.Signal(os.Interrupt); err != nil {
return fmt.Errorf("failed to kill vault server: %v", err)
}
}
// wait for the process to exit to be sure that the data dir can be
// deleted on all platforms.
if err := v.cmd.Wait(); err != nil {
return err
}
if v.returnPortsFn != nil {
v.returnPortsFn()
}
return nil
}

53
go.mod
View File

@ -7,43 +7,23 @@ replace github.com/hashicorp/consul/api => ./api
replace github.com/hashicorp/consul/sdk => ./sdk
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest v10.15.3+incompatible // indirect
github.com/Jeffail/gabs v1.1.0 // indirect
github.com/Microsoft/go-winio v0.4.3 // indirect
github.com/NYTimes/gziphandler v1.0.1
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/SAP/go-hdb v0.12.0 // indirect
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878
github.com/armon/go-radix v1.0.0
github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f // indirect
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
github.com/coredns/coredns v1.1.2
github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f // indirect
github.com/digitalocean/godo v1.10.0 // indirect
github.com/docker/go-connections v0.3.0
github.com/docker/go-units v0.3.3 // indirect
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 // indirect
github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0
github.com/envoyproxy/go-control-plane v0.8.0
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 // indirect
github.com/go-ldap/ldap v3.0.2+incompatible // indirect
github.com/go-ole/go-ole v1.2.1 // indirect
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 // indirect
github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 // indirect
github.com/gogo/googleapis v1.1.0
github.com/gogo/protobuf v1.2.1
github.com/golang/protobuf v1.3.1
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/google/go-github v17.0.0+incompatible // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/hashicorp/consul/api v1.2.0
github.com/hashicorp/consul/sdk v0.2.0
github.com/hashicorp/go-bexpr v0.1.2
@ -54,14 +34,14 @@ require (
github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71
github.com/hashicorp/go-msgpack v0.5.5
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116
github.com/hashicorp/go-plugin v1.0.1
github.com/hashicorp/go-raftchunking v0.6.1
github.com/hashicorp/go-sockaddr v1.0.0
github.com/hashicorp/go-sockaddr v1.0.2
github.com/hashicorp/go-syslog v1.0.0
github.com/hashicorp/go-uuid v1.0.1
github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031
github.com/hashicorp/golang-lru v0.5.0
github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157
github.com/hashicorp/go-version v1.1.0
github.com/hashicorp/golang-lru v0.5.1
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5
github.com/hashicorp/logutils v1.0.0
github.com/hashicorp/mdns v1.0.1 // indirect
@ -70,14 +50,10 @@ require (
github.com/hashicorp/raft v1.1.1
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea
github.com/hashicorp/serf v0.8.2
github.com/hashicorp/vault v0.10.3
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1 // indirect
github.com/hashicorp/vault/api v1.0.4
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
github.com/imdario/mergo v0.3.6
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b // indirect
github.com/kr/text v0.1.0
github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 // indirect
github.com/miekg/dns v1.0.14
github.com/mitchellh/cli v1.0.0
github.com/mitchellh/copystructure v1.0.0
@ -85,34 +61,23 @@ require (
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452
github.com/mitchellh/mapstructure v1.1.2
github.com/mitchellh/reflectwalk v1.0.1
github.com/oklog/run v0.0.0-20180308005104-6934b124db28 // indirect
github.com/onsi/gomega v1.4.2 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/ory/dockertest v3.3.4+incompatible // indirect
github.com/pascaldekloe/goe v0.1.0
github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8 // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect
github.com/ryanuber/columnize v2.1.0+incompatible
github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
google.golang.org/grpc v1.23.0
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 // indirect
gopkg.in/ory-am/dockertest.v3 v3.3.4 // indirect
gopkg.in/square/go-jose.v2 v2.3.1
gotest.tools v2.2.0+incompatible // indirect
k8s.io/api v0.0.0-20190325185214-7544f9db76f6
k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841
k8s.io/client-go v8.0.0+incompatible

134
go.sum
View File

@ -2,26 +2,16 @@ cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/azure-sdk-for-go v16.0.0+incompatible h1:gr1qKY/Ll72VjFTZmaBwRK1yQHAxCnV25ekOKroc9ws=
github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v10.15.3+incompatible h1:nhKI/bvazIs3C3TFGoSqKY6hZ8f5od5mb5/UcS6HVIY=
github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Jeffail/gabs v1.1.0 h1:kw5zCcl9tlJNHTDme7qbi21fDHZmXrnjMoXos3Jw/NI=
github.com/Jeffail/gabs v1.1.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Microsoft/go-winio v0.4.3 h1:M3NHMuPgMSUPdE5epwNUHlRPSVzHs8HpRTrVXhR0myo=
github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/NYTimes/gziphandler v1.0.1 h1:iLrQrdwjDd52kHDA5op2UBJFjmOb9g+7scBan4RN8F0=
github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/SAP/go-hdb v0.12.0 h1:5hBQZ2jjyZ268qjDmoDZJuCyLzR6oRLI60eYzmTW9m4=
github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc h1:LkkwnbY+S8WmwkWq1SVyRWMH9nYWO1P5XN3OD1tts/w=
github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
@ -36,36 +26,24 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f h1:/8NcnxL60YFll4ehCwibKotx0BR9v2ND40fomga8qDs=
github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.15.24 h1:xLAdTA/ore6xdPAljzZRed7IGqQgC+nY+ERS5vaj4Ro=
github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coredns/coredns v1.1.2 h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638=
github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f h1:JtRnQbMXb3TcSIm1j452zI45lPMiAQ0puF8iK5EnY9M=
github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o=
github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@ -76,10 +54,6 @@ github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1
github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y=
github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk=
@ -88,8 +62,7 @@ github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZ
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 h1:bGT+Ub6bpzHl7AAYQhBrZ5nYTAH2SF/848WducU0Ao4=
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
@ -100,12 +73,9 @@ github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvI
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 h1:1LlmVz15APoKz9dnm5j2ePptburJlwEH+/v/pUuoxck=
github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 h1:G7iRamCffNivybfZvsJjtk3k2qHa73xW+OysVkukcGk=
github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
@ -120,15 +90,12 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
@ -139,12 +106,8 @@ github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca h1:wobTb8S
github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-bexpr v0.1.2 h1:ijMXI4qERbzxbCnkxmfUtwMyjrrk3y+Vt0MxojNCbBs=
@ -156,6 +119,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd h1:SynRxs8h2h7lLSA5py5a3WWkYpImhREtju0CuRd97wc=
github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd/go.mod h1:ueUgD9BeIocT7QNuvxSyJyPAM9dfifBcaWmeybb67OY=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM=
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
@ -167,30 +132,38 @@ github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116 h1:Y4V/yReWjQo/Ngyc0w6C3EKXKincp4YgvXeo8lI4LrI=
github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ=
github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE=
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
github.com/hashicorp/go-raftchunking v0.6.1 h1:moEnaG3gcwsWNyIBJoD5PCByE+Ewkqxh6N05CT+MbwA=
github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0=
github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031 h1:c3Xdf5fTpk+hqhxqCO+ymqjfUXV9+GZqNgTtlnVzDos=
github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157 h1:PJ+K03hio6ADVjEc6lFu5r866o67xEEMQ73CFdI6R2U=
github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 h1:uk280DXEbQiCOZgCOI3elFSeNxf8YIZiNsbr2pQLYD0=
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
@ -211,12 +184,13 @@ github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea h1:xykPFhrBA
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk=
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/vault v0.10.3 h1:3Hf6mwC4rggOq6ViWSoJ2yfk1oBS5ed58LLcP33gmEg=
github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1 h1:7IvvWArBoSjStPohKqHj3ksXNjcyPsyXYDIhCQw6Vtg=
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0=
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw=
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@ -225,8 +199,6 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee h1:AQ/QmCk6x8ECPpf2pkPtA4lyncEEBbs8VFnVXPYKhIs=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k=
@ -235,8 +207,6 @@ github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswD
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo=
github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@ -245,8 +215,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 h1:it29sI2IM490luSc3RAhp5WuCYnc6RtbfLVAB7nmC5M=
github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
@ -261,8 +229,12 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU=
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
@ -279,32 +251,24 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s=
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk=
github.com/oklog/run v0.0.0-20180308005104-6934b124db28 h1:Hbr3fbVPXea52oPQeP7KLSxP52g6SFaNY1IqAmUyEW0=
github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE=
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8 h1:BR6MM54q4W9pn0SySwg6yctZtBKlTdUq6a+b0kArBnE=
github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
@ -325,8 +289,10 @@ github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOe
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880 h1:1Ge4j/3uB2rxzPWD3TC+daeCw+w91z8UCUL/7WH5gn8=
@ -363,24 +329,31 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -388,7 +361,9 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c h1:hDn6jm7snBX2O7+EeTk6Q4WXJfKt7MWgtiCCRi1rBoY=
golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -398,23 +373,34 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/api v0.0.0-20180829000535-087779f1d2c9 h1:z1TeLUmxf9ws9KLICfmX+KGXTs+rjm+aGWzfsv7MZ9w=
google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
@ -429,18 +415,12 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNj
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 h1:/saqWwm73dLmuzbNhe92F0QsZ/KiFND+esHco2v1hiY=
gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/ory-am/dockertest.v3 v3.3.4 h1:oen8RiwxVNxtQ1pRoV4e4jqh6UjNsOuIZ1NXns6jdcw=
gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=

View File

@ -1,277 +0,0 @@
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package civil implements types for civil time, a time-zone-independent
// representation of time that follows the rules of the proleptic
// Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
// minutes.
//
// Because they lack location information, these types do not represent unique
// moments or intervals of time. Use time.Time for that purpose.
package civil
import (
"fmt"
"time"
)
// A Date represents a date (year, month, day).
//
// This type does not include location information, and therefore does not
// describe a unique 24-hour timespan.
type Date struct {
Year int // Year (e.g., 2014).
Month time.Month // Month of the year (January = 1, ...).
Day int // Day of the month, starting at 1.
}
// DateOf returns the Date in which a time occurs in that time's location.
func DateOf(t time.Time) Date {
var d Date
d.Year, d.Month, d.Day = t.Date()
return d
}
// ParseDate parses a string in RFC3339 full-date format and returns the date value it represents.
func ParseDate(s string) (Date, error) {
t, err := time.Parse("2006-01-02", s)
if err != nil {
return Date{}, err
}
return DateOf(t), nil
}
// String returns the date in RFC3339 full-date format.
func (d Date) String() string {
return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
}
// IsValid reports whether the date is valid.
func (d Date) IsValid() bool {
return DateOf(d.In(time.UTC)) == d
}
// In returns the time corresponding to time 00:00:00 of the date in the location.
//
// In is always consistent with time.Date, even when time.Date returns a time
// on a different day. For example, if loc is America/Indiana/Vincennes, then both
// time.Date(1955, time.May, 1, 0, 0, 0, 0, loc)
// and
// civil.Date{Year: 1955, Month: time.May, Day: 1}.In(loc)
// return 23:00:00 on April 30, 1955.
//
// In panics if loc is nil.
func (d Date) In(loc *time.Location) time.Time {
return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc)
}
// AddDays returns the date that is n days in the future.
// n can also be negative to go into the past.
func (d Date) AddDays(n int) Date {
return DateOf(d.In(time.UTC).AddDate(0, 0, n))
}
// DaysSince returns the signed number of days between the date and s, not including the end day.
// This is the inverse operation to AddDays.
func (d Date) DaysSince(s Date) (days int) {
// We convert to Unix time so we do not have to worry about leap seconds:
// Unix time increases by exactly 86400 seconds per day.
deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix()
return int(deltaUnix / 86400)
}
// Before reports whether d1 occurs before d2.
func (d1 Date) Before(d2 Date) bool {
if d1.Year != d2.Year {
return d1.Year < d2.Year
}
if d1.Month != d2.Month {
return d1.Month < d2.Month
}
return d1.Day < d2.Day
}
// After reports whether d1 occurs after d2.
func (d1 Date) After(d2 Date) bool {
return d2.Before(d1)
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of d.String().
func (d Date) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The date is expected to be a string in a format accepted by ParseDate.
func (d *Date) UnmarshalText(data []byte) error {
var err error
*d, err = ParseDate(string(data))
return err
}
// A Time represents a time with nanosecond precision.
//
// This type does not include location information, and therefore does not
// describe a unique moment in time.
//
// This type exists to represent the TIME type in storage-based APIs like BigQuery.
// Most operations on Times are unlikely to be meaningful. Prefer the DateTime type.
type Time struct {
Hour int // The hour of the day in 24-hour format; range [0-23]
Minute int // The minute of the hour; range [0-59]
Second int // The second of the minute; range [0-59]
Nanosecond int // The nanosecond of the second; range [0-999999999]
}
// TimeOf returns the Time representing the time of day in which a time occurs
// in that time's location. It ignores the date.
func TimeOf(t time.Time) Time {
var tm Time
tm.Hour, tm.Minute, tm.Second = t.Clock()
tm.Nanosecond = t.Nanosecond()
return tm
}
// ParseTime parses a string and returns the time value it represents.
// ParseTime accepts an extended form of the RFC3339 partial-time format. After
// the HH:MM:SS part of the string, an optional fractional part may appear,
// consisting of a decimal point followed by one to nine decimal digits.
// (RFC3339 admits only one digit after the decimal point).
func ParseTime(s string) (Time, error) {
t, err := time.Parse("15:04:05.999999999", s)
if err != nil {
return Time{}, err
}
return TimeOf(t), nil
}
// String returns the date in the format described in ParseTime. If Nanoseconds
// is zero, no fractional part will be generated. Otherwise, the result will
// end with a fractional part consisting of a decimal point and nine digits.
func (t Time) String() string {
s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
if t.Nanosecond == 0 {
return s
}
return s + fmt.Sprintf(".%09d", t.Nanosecond)
}
// IsValid reports whether the time is valid.
func (t Time) IsValid() bool {
// Construct a non-zero time.
tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
return TimeOf(tm) == t
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of t.String().
func (t Time) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The time is expected to be a string in a format accepted by ParseTime.
func (t *Time) UnmarshalText(data []byte) error {
var err error
*t, err = ParseTime(string(data))
return err
}
// A DateTime represents a date and time.
//
// This type does not include location information, and therefore does not
// describe a unique moment in time.
type DateTime struct {
Date Date
Time Time
}
// Note: We deliberately do not embed Date into DateTime, to avoid promoting AddDays and Sub.
// DateTimeOf returns the DateTime in which a time occurs in that time's location.
func DateTimeOf(t time.Time) DateTime {
return DateTime{
Date: DateOf(t),
Time: TimeOf(t),
}
}
// ParseDateTime parses a string and returns the DateTime it represents.
// ParseDateTime accepts a variant of the RFC3339 date-time format that omits
// the time offset but includes an optional fractional time, as described in
// ParseTime. Informally, the accepted format is
// YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
// where the 'T' may be a lower-case 't'.
func ParseDateTime(s string) (DateTime, error) {
t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
if err != nil {
t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
if err != nil {
return DateTime{}, err
}
}
return DateTimeOf(t), nil
}
// String returns the date in the format described in ParseDate.
func (dt DateTime) String() string {
return dt.Date.String() + "T" + dt.Time.String()
}
// IsValid reports whether the datetime is valid.
func (dt DateTime) IsValid() bool {
return dt.Date.IsValid() && dt.Time.IsValid()
}
// In returns the time corresponding to the DateTime in the given location.
//
// If the time is missing or ambigous at the location, In returns the same
// result as time.Date. For example, if loc is America/Indiana/Vincennes, then
// both
// time.Date(1955, time.May, 1, 0, 30, 0, 0, loc)
// and
// civil.DateTime{
// civil.Date{Year: 1955, Month: time.May, Day: 1}},
// civil.Time{Minute: 30}}.In(loc)
// return 23:30:00 on April 30, 1955.
//
// In panics if loc is nil.
func (dt DateTime) In(loc *time.Location) time.Time {
return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc)
}
// Before reports whether dt1 occurs before dt2.
func (dt1 DateTime) Before(dt2 DateTime) bool {
return dt1.In(time.UTC).Before(dt2.In(time.UTC))
}
// After reports whether dt1 occurs after dt2.
func (dt1 DateTime) After(dt2 DateTime) bool {
return dt2.Before(dt1)
}
// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of dt.String().
func (dt DateTime) MarshalText() ([]byte, error) {
return []byte(dt.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The datetime is expected to be a string in a format accepted by ParseDateTime
func (dt *DateTime) UnmarshalText(data []byte) error {
var err error
*dt, err = ParseDateTime(string(data))
return err
}

View File

@ -1,19 +0,0 @@
Copyright (c) 2014 Ashley Jeffs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,315 +0,0 @@
![Gabs](gabs_logo.png "Gabs")
Gabs is a small utility for dealing with dynamic or unknown JSON structures in
golang. It's pretty much just a helpful wrapper around the golang
`json.Marshal/json.Unmarshal` behaviour and `map[string]interface{}` objects.
It does nothing spectacular except for being fabulous.
https://godoc.org/github.com/Jeffail/gabs
## How to install:
``` bash
go get github.com/Jeffail/gabs
```
## How to use
### Parsing and searching JSON
``` go
...
import "github.com/Jeffail/gabs"
jsonParsed, err := gabs.ParseJSON([]byte(`{
"outter":{
"inner":{
"value1":10,
"value2":22
},
"alsoInner":{
"value1":20
}
}
}`))
var value float64
var ok bool
value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false
exists := jsonParsed.Exists("outter", "inner", "value1")
// exists == true
exists := jsonParsed.Exists("does", "not", "exist")
// exists == false
exists := jsonParsed.ExistsP("does.not.exist")
// exists == false
...
```
### Iterating objects
``` go
...
jsonParsed, _ := gabs.ParseJSON([]byte(`{"object":{ "first": 1, "second": 2, "third": 3 }}`))
// S is shorthand for Search
children, _ := jsonParsed.S("object").ChildrenMap()
for key, child := range children {
fmt.Printf("key: %v, value: %v\n", key, child.Data().(string))
}
...
```
### Iterating arrays
``` go
...
jsonParsed, _ := gabs.ParseJSON([]byte(`{"array":[ "first", "second", "third" ]}`))
// S is shorthand for Search
children, _ := jsonParsed.S("array").Children()
for _, child := range children {
fmt.Println(child.Data().(string))
}
...
```
Will print:
```
first
second
third
```
Children() will return all children of an array in order. This also works on
objects, however, the children will be returned in a random order.
### Searching through arrays
If your JSON structure contains arrays you can still search the fields of the
objects within the array, this returns a JSON array containing the results for
each element.
``` go
...
jsonParsed, _ := gabs.ParseJSON([]byte(`{"array":[ {"value":1}, {"value":2}, {"value":3} ]}`))
fmt.Println(jsonParsed.Path("array.value").String())
...
```
Will print:
```
[1,2,3]
```
### Generating JSON
``` go
...
jsonObj := gabs.New()
// or gabs.Consume(jsonObject) to work on an existing map[string]interface{}
jsonObj.Set(10, "outter", "inner", "value")
jsonObj.SetP(20, "outter.inner.value2")
jsonObj.Set(30, "outter", "inner2", "value3")
fmt.Println(jsonObj.String())
...
```
Will print:
```
{"outter":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}}
```
To pretty-print:
``` go
...
fmt.Println(jsonObj.StringIndent("", " "))
...
```
Will print:
```
{
"outter": {
"inner": {
"value": 10,
"value2": 20
},
"inner2": {
"value3": 30
}
}
}
```
### Generating Arrays
``` go
...
jsonObj := gabs.New()
jsonObj.Array("foo", "array")
// Or .ArrayP("foo.array")
jsonObj.ArrayAppend(10, "foo", "array")
jsonObj.ArrayAppend(20, "foo", "array")
jsonObj.ArrayAppend(30, "foo", "array")
fmt.Println(jsonObj.String())
...
```
Will print:
```
{"foo":{"array":[10,20,30]}}
```
Working with arrays by index:
``` go
...
jsonObj := gabs.New()
// Create an array with the length of 3
jsonObj.ArrayOfSize(3, "foo")
jsonObj.S("foo").SetIndex("test1", 0)
jsonObj.S("foo").SetIndex("test2", 1)
// Create an embedded array with the length of 3
jsonObj.S("foo").ArrayOfSizeI(3, 2)
jsonObj.S("foo").Index(2).SetIndex(1, 0)
jsonObj.S("foo").Index(2).SetIndex(2, 1)
jsonObj.S("foo").Index(2).SetIndex(3, 2)
fmt.Println(jsonObj.String())
...
```
Will print:
```
{"foo":["test1","test2",[1,2,3]]}
```
### Converting back to JSON
This is the easiest part:
``` go
...
jsonParsedObj, _ := gabs.ParseJSON([]byte(`{
"outter":{
"values":{
"first":10,
"second":11
}
},
"outter2":"hello world"
}`))
jsonOutput := jsonParsedObj.String()
// Becomes `{"outter":{"values":{"first":10,"second":11}},"outter2":"hello world"}`
...
```
And to serialize a specific segment is as simple as:
``` go
...
jsonParsedObj := gabs.ParseJSON([]byte(`{
"outter":{
"values":{
"first":10,
"second":11
}
},
"outter2":"hello world"
}`))
jsonOutput := jsonParsedObj.Search("outter").String()
// Becomes `{"values":{"first":10,"second":11}}`
...
```
### Merge two containers
You can merge a JSON structure into an existing one, where collisions will be
converted into a JSON array.
``` go
jsonParsed1, _ := ParseJSON([]byte(`{"outter": {"value1": "one"}}`))
jsonParsed2, _ := ParseJSON([]byte(`{"outter": {"inner": {"value3": "three"}}, "outter2": {"value2": "two"}}`))
jsonParsed1.Merge(jsonParsed2)
// Becomes `{"outter":{"inner":{"value3":"three"},"value1":"one"},"outter2":{"value2":"two"}}`
```
Arrays are merged:
``` go
jsonParsed1, _ := ParseJSON([]byte(`{"array": ["one"]}`))
jsonParsed2, _ := ParseJSON([]byte(`{"array": ["two"]}`))
jsonParsed1.Merge(jsonParsed2)
// Becomes `{"array":["one", "two"]}`
```
### Parsing Numbers
Gabs uses the `json` package under the bonnet, which by default will parse all
number values into `float64`. If you need to parse `Int` values then you should
use a `json.Decoder` (https://golang.org/pkg/encoding/json/#Decoder):
``` go
sample := []byte(`{"test":{"int":10, "float":6.66}}`)
dec := json.NewDecoder(bytes.NewReader(sample))
dec.UseNumber()
val, err := gabs.ParseJSONDecoder(dec)
if err != nil {
t.Errorf("Failed to parse: %v", err)
return
}
intValue, err := val.Path("test.int").Data().(json.Number).Int64()
```

View File

@ -1,579 +0,0 @@
/*
Copyright (c) 2014 Ashley Jeffs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Package gabs implements a simplified wrapper around creating and parsing JSON.
package gabs
import (
"bytes"
"encoding/json"
"errors"
"io"
"io/ioutil"
"strings"
)
//--------------------------------------------------------------------------------------------------
var (
// ErrOutOfBounds - Index out of bounds.
ErrOutOfBounds = errors.New("out of bounds")
// ErrNotObjOrArray - The target is not an object or array type.
ErrNotObjOrArray = errors.New("not an object or array")
// ErrNotObj - The target is not an object type.
ErrNotObj = errors.New("not an object")
// ErrNotArray - The target is not an array type.
ErrNotArray = errors.New("not an array")
// ErrPathCollision - Creating a path failed because an element collided with an existing value.
ErrPathCollision = errors.New("encountered value collision whilst building path")
// ErrInvalidInputObj - The input value was not a map[string]interface{}.
ErrInvalidInputObj = errors.New("invalid input object")
// ErrInvalidInputText - The input data could not be parsed.
ErrInvalidInputText = errors.New("input text could not be parsed")
// ErrInvalidPath - The filepath was not valid.
ErrInvalidPath = errors.New("invalid file path")
// ErrInvalidBuffer - The input buffer contained an invalid JSON string
ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
)
//--------------------------------------------------------------------------------------------------
// Container - an internal structure that holds a reference to the core interface map of the parsed
// json. Use this container to move context.
type Container struct {
object interface{}
}
// Data - Return the contained data as an interface{}.
func (g *Container) Data() interface{} {
if g == nil {
return nil
}
return g.object
}
//--------------------------------------------------------------------------------------------------
// Path - Search for a value using dot notation.
func (g *Container) Path(path string) *Container {
return g.Search(strings.Split(path, ".")...)
}
// Search - Attempt to find and return an object within the JSON structure by specifying the
// hierarchy of field names to locate the target. If the search encounters an array and has not
// reached the end target then it will iterate each object of the array for the target and return
// all of the results in a JSON array.
func (g *Container) Search(hierarchy ...string) *Container {
var object interface{}
object = g.Data()
for target := 0; target < len(hierarchy); target++ {
if mmap, ok := object.(map[string]interface{}); ok {
object, ok = mmap[hierarchy[target]]
if !ok {
return nil
}
} else if marray, ok := object.([]interface{}); ok {
tmpArray := []interface{}{}
for _, val := range marray {
tmpGabs := &Container{val}
res := tmpGabs.Search(hierarchy[target:]...)
if res != nil {
tmpArray = append(tmpArray, res.Data())
}
}
if len(tmpArray) == 0 {
return nil
}
return &Container{tmpArray}
} else {
return nil
}
}
return &Container{object}
}
// S - Shorthand method, does the same thing as Search.
func (g *Container) S(hierarchy ...string) *Container {
return g.Search(hierarchy...)
}
// Exists - Checks whether a path exists.
func (g *Container) Exists(hierarchy ...string) bool {
return g.Search(hierarchy...) != nil
}
// ExistsP - Checks whether a dot notation path exists.
func (g *Container) ExistsP(path string) bool {
return g.Exists(strings.Split(path, ".")...)
}
// Index - Attempt to find and return an object within a JSON array by index.
func (g *Container) Index(index int) *Container {
if array, ok := g.Data().([]interface{}); ok {
if index >= len(array) {
return &Container{nil}
}
return &Container{array[index]}
}
return &Container{nil}
}
// Children - Return a slice of all the children of the array. This also works for objects, however,
// the children returned for an object will NOT be in order and you lose the names of the returned
// objects this way.
func (g *Container) Children() ([]*Container, error) {
if array, ok := g.Data().([]interface{}); ok {
children := make([]*Container, len(array))
for i := 0; i < len(array); i++ {
children[i] = &Container{array[i]}
}
return children, nil
}
if mmap, ok := g.Data().(map[string]interface{}); ok {
children := []*Container{}
for _, obj := range mmap {
children = append(children, &Container{obj})
}
return children, nil
}
return nil, ErrNotObjOrArray
}
// ChildrenMap - Return a map of all the children of an object.
func (g *Container) ChildrenMap() (map[string]*Container, error) {
if mmap, ok := g.Data().(map[string]interface{}); ok {
children := map[string]*Container{}
for name, obj := range mmap {
children[name] = &Container{obj}
}
return children, nil
}
return nil, ErrNotObj
}
//--------------------------------------------------------------------------------------------------
// Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be
// constructed, and if a collision occurs with a non object type whilst iterating the path an error
// is returned.
func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
if len(path) == 0 {
g.object = value
return g, nil
}
var object interface{}
if g.object == nil {
g.object = map[string]interface{}{}
}
object = g.object
for target := 0; target < len(path); target++ {
if mmap, ok := object.(map[string]interface{}); ok {
if target == len(path)-1 {
mmap[path[target]] = value
} else if mmap[path[target]] == nil {
mmap[path[target]] = map[string]interface{}{}
}
object = mmap[path[target]]
} else {
return &Container{nil}, ErrPathCollision
}
}
return &Container{object}, nil
}
// SetP - Does the same as Set, but using a dot notation JSON path.
func (g *Container) SetP(value interface{}, path string) (*Container, error) {
return g.Set(value, strings.Split(path, ".")...)
}
// SetIndex - Set a value of an array element based on the index.
func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
if array, ok := g.Data().([]interface{}); ok {
if index >= len(array) {
return &Container{nil}, ErrOutOfBounds
}
array[index] = value
return &Container{array[index]}, nil
}
return &Container{nil}, ErrNotArray
}
// Object - Create a new JSON object at a path. Returns an error if the path contains a collision
// with a non object type.
func (g *Container) Object(path ...string) (*Container, error) {
return g.Set(map[string]interface{}{}, path...)
}
// ObjectP - Does the same as Object, but using a dot notation JSON path.
func (g *Container) ObjectP(path string) (*Container, error) {
return g.Object(strings.Split(path, ".")...)
}
// ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an
// array or the index is out of bounds.
func (g *Container) ObjectI(index int) (*Container, error) {
return g.SetIndex(map[string]interface{}{}, index)
}
// Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
// a non object type.
func (g *Container) Array(path ...string) (*Container, error) {
return g.Set([]interface{}{}, path...)
}
// ArrayP - Does the same as Array, but using a dot notation JSON path.
func (g *Container) ArrayP(path string) (*Container, error) {
return g.Array(strings.Split(path, ".")...)
}
// ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an
// array or the index is out of bounds.
func (g *Container) ArrayI(index int) (*Container, error) {
return g.SetIndex([]interface{}{}, index)
}
// ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the
// path contains a collision with a non object type.
func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
a := make([]interface{}, size)
return g.Set(a, path...)
}
// ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) {
return g.ArrayOfSize(size, strings.Split(path, ".")...)
}
// ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error
// if the object is not an array or the index is out of bounds.
func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
a := make([]interface{}, size)
return g.SetIndex(a, index)
}
// Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
func (g *Container) Delete(path ...string) error {
var object interface{}
if g.object == nil {
return ErrNotObj
}
object = g.object
for target := 0; target < len(path); target++ {
if mmap, ok := object.(map[string]interface{}); ok {
if target == len(path)-1 {
if _, ok := mmap[path[target]]; ok {
delete(mmap, path[target])
} else {
return ErrNotObj
}
}
object = mmap[path[target]]
} else {
return ErrNotObj
}
}
return nil
}
// DeleteP - Does the same as Delete, but using a dot notation JSON path.
func (g *Container) DeleteP(path string) error {
return g.Delete(strings.Split(path, ".")...)
}
// Merge - Merges two gabs-containers
func (g *Container) Merge(toMerge *Container) error {
var recursiveFnc func(map[string]interface{}, []string) error
recursiveFnc = func(mmap map[string]interface{}, path []string) error {
for key, value := range mmap {
newPath := append(path, key)
if g.Exists(newPath...) {
target := g.Search(newPath...)
switch t := value.(type) {
case map[string]interface{}:
switch targetV := target.Data().(type) {
case map[string]interface{}:
if err := recursiveFnc(t, newPath); err != nil {
return err
}
case []interface{}:
g.Set(append(targetV, t), newPath...)
default:
newSlice := append([]interface{}{}, targetV)
g.Set(append(newSlice, t), newPath...)
}
case []interface{}:
for _, valueOfSlice := range t {
if err := g.ArrayAppend(valueOfSlice, newPath...); err != nil {
return err
}
}
default:
switch targetV := target.Data().(type) {
case []interface{}:
g.Set(append(targetV, t), newPath...)
default:
newSlice := append([]interface{}{}, targetV)
g.Set(append(newSlice, t), newPath...)
}
}
} else {
// path doesn't exist. So set the value
if _, err := g.Set(value, newPath...); err != nil {
return err
}
}
}
return nil
}
if mmap, ok := toMerge.Data().(map[string]interface{}); ok {
return recursiveFnc(mmap, []string{})
}
return nil
}
//--------------------------------------------------------------------------------------------------
/*
Array modification/search - Keeping these options simple right now, no need for anything more
complicated since you can just cast to []interface{}, modify and then reassign with Set.
*/
// ArrayAppend - Append a value onto a JSON array. If the target is not a JSON array then it will be
// converted into one, with its contents as the first element of the array.
func (g *Container) ArrayAppend(value interface{}, path ...string) error {
if array, ok := g.Search(path...).Data().([]interface{}); ok {
array = append(array, value)
_, err := g.Set(array, path...)
return err
}
newArray := []interface{}{}
newArray = append(newArray, g.Search(path...).Data())
newArray = append(newArray, value)
_, err := g.Set(newArray, path...)
return err
}
// ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
func (g *Container) ArrayAppendP(value interface{}, path string) error {
return g.ArrayAppend(value, strings.Split(path, ".")...)
}
// ArrayRemove - Remove an element from a JSON array.
func (g *Container) ArrayRemove(index int, path ...string) error {
if index < 0 {
return ErrOutOfBounds
}
array, ok := g.Search(path...).Data().([]interface{})
if !ok {
return ErrNotArray
}
if index < len(array) {
array = append(array[:index], array[index+1:]...)
} else {
return ErrOutOfBounds
}
_, err := g.Set(array, path...)
return err
}
// ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
func (g *Container) ArrayRemoveP(index int, path string) error {
return g.ArrayRemove(index, strings.Split(path, ".")...)
}
// ArrayElement - Access an element from a JSON array.
func (g *Container) ArrayElement(index int, path ...string) (*Container, error) {
if index < 0 {
return &Container{nil}, ErrOutOfBounds
}
array, ok := g.Search(path...).Data().([]interface{})
if !ok {
return &Container{nil}, ErrNotArray
}
if index < len(array) {
return &Container{array[index]}, nil
}
return &Container{nil}, ErrOutOfBounds
}
// ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
func (g *Container) ArrayElementP(index int, path string) (*Container, error) {
return g.ArrayElement(index, strings.Split(path, ".")...)
}
// ArrayCount - Count the number of elements in a JSON array.
func (g *Container) ArrayCount(path ...string) (int, error) {
if array, ok := g.Search(path...).Data().([]interface{}); ok {
return len(array), nil
}
return 0, ErrNotArray
}
// ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
func (g *Container) ArrayCountP(path string) (int, error) {
return g.ArrayCount(strings.Split(path, ".")...)
}
//--------------------------------------------------------------------------------------------------
// Bytes - Converts the contained object back to a JSON []byte blob.
func (g *Container) Bytes() []byte {
if g.Data() != nil {
if bytes, err := json.Marshal(g.object); err == nil {
return bytes
}
}
return []byte("{}")
}
// BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent.
func (g *Container) BytesIndent(prefix string, indent string) []byte {
if g.object != nil {
if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil {
return bytes
}
}
return []byte("{}")
}
// String - Converts the contained object to a JSON formatted string.
func (g *Container) String() string {
return string(g.Bytes())
}
// StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent.
func (g *Container) StringIndent(prefix string, indent string) string {
return string(g.BytesIndent(prefix, indent))
}
// EncodeOpt is a functional option for the EncodeJSON method.
type EncodeOpt func(e *json.Encoder)
// EncodeOptHTMLEscape sets the encoder to escape the JSON for html.
func EncodeOptHTMLEscape(doEscape bool) EncodeOpt {
return func(e *json.Encoder) {
e.SetEscapeHTML(doEscape)
}
}
// EncodeOptIndent sets the encoder to indent the JSON output.
func EncodeOptIndent(prefix string, indent string) EncodeOpt {
return func(e *json.Encoder) {
e.SetIndent(prefix, indent)
}
}
// EncodeJSON - Encodes the contained object back to a JSON formatted []byte
// using a variant list of modifier functions for the encoder being used.
// Functions for modifying the output are prefixed with EncodeOpt, e.g.
// EncodeOptHTMLEscape.
func (g *Container) EncodeJSON(encodeOpts ...EncodeOpt) []byte {
var b bytes.Buffer
encoder := json.NewEncoder(&b)
encoder.SetEscapeHTML(false) // Do not escape by default.
for _, opt := range encodeOpts {
opt(encoder)
}
if err := encoder.Encode(g.object); err != nil {
return []byte("{}")
}
result := b.Bytes()
if len(result) > 0 {
result = result[:len(result)-1]
}
return result
}
// New - Create a new gabs JSON object.
func New() *Container {
return &Container{map[string]interface{}{}}
}
// Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
func Consume(root interface{}) (*Container, error) {
return &Container{root}, nil
}
// ParseJSON - Convert a string into a representation of the parsed JSON.
func ParseJSON(sample []byte) (*Container, error) {
var gabs Container
if err := json.Unmarshal(sample, &gabs.object); err != nil {
return nil, err
}
return &gabs, nil
}
// ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
var gabs Container
if err := decoder.Decode(&gabs.object); err != nil {
return nil, err
}
return &gabs, nil
}
// ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
func ParseJSONFile(path string) (*Container, error) {
if len(path) > 0 {
cBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
container, err := ParseJSON(cBytes)
if err != nil {
return nil, err
}
return container, nil
}
return nil, ErrInvalidPath
}
// ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
var gabs Container
jsonDecoder := json.NewDecoder(buffer)
if err := jsonDecoder.Decode(&gabs.object); err != nil {
return nil, err
}
return &gabs, nil
}
//--------------------------------------------------------------------------------------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

201
vendor/github.com/SAP/go-hdb/LICENSE generated vendored
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,5 +0,0 @@
SAP HANA Database driver for the Go Programming Language
Copyright 2014 SAP SE
This product includes software developed at
SAP SE (http://www.sap.com).

View File

@ -1,43 +0,0 @@
/*
Copyright 2017 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"database/sql/driver"
)
// NullBytes represents an []byte that may be null.
// NullBytes implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullBytes struct {
Bytes []byte
Valid bool // Valid is true if Bytes is not NULL
}
// Scan implements the Scanner interface.
func (n *NullBytes) Scan(value interface{}) error {
n.Bytes, n.Valid = value.([]byte)
return nil
}
// Value implements the driver Valuer interface.
func (n NullBytes) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Bytes, nil
}

View File

@ -1,287 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"context"
"crypto/tls"
"crypto/x509"
"database/sql/driver"
"fmt"
"io/ioutil"
"net/url"
"strconv"
"sync"
)
/*
A Connector represents a hdb driver in a fixed configuration.
A Connector can be passed to sql.OpenDB (starting from go 1.10) allowing users to bypass a string based data source name.
*/
type Connector struct {
mu sync.RWMutex
host, username, password string
locale string
bufferSize, fetchSize, timeout int
tlsConfig *tls.Config
}
func newConnector() *Connector {
return &Connector{
fetchSize: DefaultFetchSize,
timeout: DefaultTimeout,
}
}
// NewBasicAuthConnector creates a connector for basic authentication.
func NewBasicAuthConnector(host, username, password string) *Connector {
c := newConnector()
c.host = host
c.username = username
c.password = password
return c
}
// NewDSNConnector creates a connector from a data source name.
func NewDSNConnector(dsn string) (*Connector, error) {
c := newConnector()
url, err := url.Parse(dsn)
if err != nil {
return nil, err
}
c.host = url.Host
if url.User != nil {
c.username = url.User.Username()
c.password, _ = url.User.Password()
}
var certPool *x509.CertPool
for k, v := range url.Query() {
switch k {
default:
return nil, fmt.Errorf("URL parameter %s is not supported", k)
case DSNFetchSize:
if len(v) == 0 {
continue
}
fetchSize, err := strconv.Atoi(v[0])
if err != nil {
return nil, fmt.Errorf("failed to parse fetchSize: %s", v[0])
}
if fetchSize < minFetchSize {
c.fetchSize = minFetchSize
} else {
c.fetchSize = fetchSize
}
case DSNTimeout:
if len(v) == 0 {
continue
}
timeout, err := strconv.Atoi(v[0])
if err != nil {
return nil, fmt.Errorf("failed to parse timeout: %s", v[0])
}
if timeout < minTimeout {
c.timeout = minTimeout
} else {
c.timeout = timeout
}
case DSNLocale:
if len(v) == 0 {
continue
}
c.locale = v[0]
case DSNTLSServerName:
if len(v) == 0 {
continue
}
if c.tlsConfig == nil {
c.tlsConfig = &tls.Config{}
}
c.tlsConfig.ServerName = v[0]
case DSNTLSInsecureSkipVerify:
if len(v) == 0 {
continue
}
var err error
b := true
if v[0] != "" {
b, err = strconv.ParseBool(v[0])
if err != nil {
return nil, fmt.Errorf("failed to parse InsecureSkipVerify (bool): %s", v[0])
}
}
if c.tlsConfig == nil {
c.tlsConfig = &tls.Config{}
}
c.tlsConfig.InsecureSkipVerify = b
case DSNTLSRootCAFile:
for _, fn := range v {
rootPEM, err := ioutil.ReadFile(fn)
if err != nil {
return nil, err
}
if certPool == nil {
certPool = x509.NewCertPool()
}
if ok := certPool.AppendCertsFromPEM(rootPEM); !ok {
return nil, fmt.Errorf("failed to parse root certificate - filename: %s", fn)
}
}
if certPool != nil {
if c.tlsConfig == nil {
c.tlsConfig = &tls.Config{}
}
c.tlsConfig.RootCAs = certPool
}
}
}
return c, nil
}
// Host returns the host of the connector.
func (c *Connector) Host() string {
return c.host
}
// Username returns the username of the connector.
func (c *Connector) Username() string {
return c.username
}
// Password returns the password of the connector.
func (c *Connector) Password() string {
return c.password
}
// Locale returns the locale of the connector.
func (c *Connector) Locale() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.locale
}
/*
SetLocale sets the locale of the connector.
For more information please see DSNLocale.
*/
func (c *Connector) SetLocale(locale string) {
c.mu.Lock()
c.locale = locale
c.mu.Unlock()
}
// FetchSize returns the fetchSize of the connector.
func (c *Connector) FetchSize() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.fetchSize
}
/*
SetFetchSize sets the fetchSize of the connector.
For more information please see DSNFetchSize.
*/
func (c *Connector) SetFetchSize(fetchSize int) error {
c.mu.Lock()
defer c.mu.Unlock()
if fetchSize < minFetchSize {
fetchSize = minFetchSize
}
c.fetchSize = fetchSize
return nil
}
// Timeout returns the timeout of the connector.
func (c *Connector) Timeout() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.timeout
}
/*
SetTimeout sets the timeout of the connector.
For more information please see DSNTimeout.
*/
func (c *Connector) SetTimeout(timeout int) error {
c.mu.Lock()
defer c.mu.Unlock()
if timeout < minTimeout {
timeout = minTimeout
}
c.timeout = timeout
return nil
}
// TLSConfig returns the TLS configuration of the connector.
func (c *Connector) TLSConfig() *tls.Config {
c.mu.RLock()
defer c.mu.RUnlock()
return c.tlsConfig
}
// SetTLSConfig sets the TLS configuration of the connector.
func (c *Connector) SetTLSConfig(tlsConfig *tls.Config) error {
c.mu.Lock()
defer c.mu.Unlock()
c.tlsConfig = tlsConfig
return nil
}
// BasicAuthDSN return the connector DSN for basic authentication.
func (c *Connector) BasicAuthDSN() string {
values := url.Values{}
if c.locale != "" {
values.Set(DSNLocale, c.locale)
}
if c.fetchSize != 0 {
values.Set(DSNFetchSize, fmt.Sprintf("%d", c.fetchSize))
}
if c.timeout != 0 {
values.Set(DSNTimeout, fmt.Sprintf("%d", c.timeout))
}
return (&url.URL{
Scheme: DriverName,
User: url.UserPassword(c.username, c.password),
Host: c.host,
RawQuery: values.Encode(),
}).String()
}
// Connect implements the database/sql/driver/Connector interface.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
return newConn(ctx, c)
}
// Driver implements the database/sql/driver/Connector interface.
func (c *Connector) Driver() driver.Driver {
return drv
}

View File

@ -1,363 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"database/sql/driver"
"errors"
"fmt"
"math"
"reflect"
"time"
p "github.com/SAP/go-hdb/internal/protocol"
)
const (
minTinyint = 0
maxTinyint = math.MaxUint8
minSmallint = math.MinInt16
maxSmallint = math.MaxInt16
minInteger = math.MinInt32
maxInteger = math.MaxInt32
minBigint = math.MinInt64
maxBigint = math.MaxInt64
maxReal = math.MaxFloat32
maxDouble = math.MaxFloat64
)
// ErrIntegerOutOfRange means that an integer exceeds the size of the hdb integer field.
var ErrIntegerOutOfRange = errors.New("integer out of range error")
// ErrFloatOutOfRange means that a float exceeds the size of the hdb float field.
var ErrFloatOutOfRange = errors.New("float out of range error")
var typeOfTime = reflect.TypeOf((*time.Time)(nil)).Elem()
var typeOfBytes = reflect.TypeOf((*[]byte)(nil)).Elem()
func checkNamedValue(prmFieldSet *p.ParameterFieldSet, nv *driver.NamedValue) error {
idx := nv.Ordinal - 1
if idx >= prmFieldSet.NumInputField() {
return nil
}
f := prmFieldSet.Field(idx)
dt := f.TypeCode().DataType()
value, err := convertNamedValue(idx, f, dt, nv.Value)
if err != nil {
return err
}
nv.Value = value
return nil
}
func convertNamedValue(idx int, f *p.ParameterField, dt p.DataType, v driver.Value) (driver.Value, error) {
var err error
// let fields with own Value converter convert themselves first (e.g. NullInt64, ...)
if _, ok := v.(driver.Valuer); ok {
if v, err = driver.DefaultParameterConverter.ConvertValue(v); err != nil {
return nil, err
}
}
switch dt {
default:
return nil, fmt.Errorf("convert named value datatype error: %[1]d - %[1]s", dt)
case p.DtTinyint:
return convertNvInteger(v, minTinyint, maxTinyint)
case p.DtSmallint:
return convertNvInteger(v, minSmallint, maxSmallint)
case p.DtInteger:
return convertNvInteger(v, minInteger, maxInteger)
case p.DtBigint:
return convertNvInteger(v, minBigint, maxBigint)
case p.DtReal:
return convertNvFloat(v, maxReal)
case p.DtDouble:
return convertNvFloat(v, maxDouble)
case p.DtTime:
return convertNvTime(v)
case p.DtDecimal:
return convertNvDecimal(v)
case p.DtString:
return convertNvString(v)
case p.DtBytes:
return convertNvBytes(v)
case p.DtLob:
return convertNvLob(idx, f, v)
}
}
// integer types
func convertNvInteger(v interface{}, min, max int64) (driver.Value, error) {
if v == nil {
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
// bool is represented in HDB as tinyint
case reflect.Bool:
return rv.Bool(), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i64 := rv.Int()
if i64 > max || i64 < min {
return nil, ErrIntegerOutOfRange
}
return i64, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
u64 := rv.Uint()
if u64 > uint64(max) {
return nil, ErrIntegerOutOfRange
}
return int64(u64), nil
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvInteger(rv.Elem().Interface(), min, max)
}
return nil, fmt.Errorf("unsupported integer conversion type error %[1]T %[1]v", v)
}
// float types
func convertNvFloat(v interface{}, max float64) (driver.Value, error) {
if v == nil {
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Float32, reflect.Float64:
f64 := rv.Float()
if math.Abs(f64) > max {
return nil, ErrFloatOutOfRange
}
return f64, nil
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvFloat(rv.Elem().Interface(), max)
}
return nil, fmt.Errorf("unsupported float conversion type error %[1]T %[1]v", v)
}
// time
func convertNvTime(v interface{}) (driver.Value, error) {
if v == nil {
return nil, nil
}
switch v := v.(type) {
case time.Time:
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvTime(rv.Elem().Interface())
}
if rv.Type().ConvertibleTo(typeOfTime) {
tv := rv.Convert(typeOfTime)
return tv.Interface().(time.Time), nil
}
return nil, fmt.Errorf("unsupported time conversion type error %[1]T %[1]v", v)
}
// decimal
func convertNvDecimal(v interface{}) (driver.Value, error) {
if v == nil {
return nil, nil
}
if v, ok := v.([]byte); ok {
return v, nil
}
return nil, fmt.Errorf("unsupported decimal conversion type error %[1]T %[1]v", v)
}
// string
func convertNvString(v interface{}) (driver.Value, error) {
if v == nil {
return v, nil
}
switch v := v.(type) {
case string, []byte:
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.String:
return rv.String(), nil
case reflect.Slice:
if rv.Type() == typeOfBytes {
return rv.Bytes(), nil
}
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvString(rv.Elem().Interface())
}
if rv.Type().ConvertibleTo(typeOfBytes) {
bv := rv.Convert(typeOfBytes)
return bv.Interface().([]byte), nil
}
return nil, fmt.Errorf("unsupported character conversion type error %[1]T %[1]v", v)
}
// bytes
func convertNvBytes(v interface{}) (driver.Value, error) {
if v == nil {
return v, nil
}
if v, ok := v.([]byte); ok {
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Slice:
if rv.Type() == typeOfBytes {
return rv.Bytes(), nil
}
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvBytes(rv.Elem().Interface())
}
if rv.Type().ConvertibleTo(typeOfBytes) {
bv := rv.Convert(typeOfBytes)
return bv.Interface().([]byte), nil
}
return nil, fmt.Errorf("unsupported bytes conversion type error %[1]T %[1]v", v)
}
// Lob
func convertNvLob(idx int, f *p.ParameterField, v interface{}) (driver.Value, error) {
if v == nil {
return v, nil
}
switch v := v.(type) {
case Lob:
if v.rd == nil {
return nil, fmt.Errorf("lob error: initial reader %[1]T %[1]v", v)
}
f.SetLobReader(v.rd)
return fmt.Sprintf("<lob %d", idx), nil
case *Lob:
if v.rd == nil {
return nil, fmt.Errorf("lob error: initial reader %[1]T %[1]v", v)
}
f.SetLobReader(v.rd)
return fmt.Sprintf("<lob %d", idx), nil
case NullLob:
if !v.Valid {
return nil, nil
}
if v.Lob.rd == nil {
return nil, fmt.Errorf("lob error: initial reader %[1]T %[1]v", v)
}
f.SetLobReader(v.Lob.rd)
return fmt.Sprintf("<lob %d", idx), nil
case *NullLob:
if !v.Valid {
return nil, nil
}
if v.Lob.rd == nil {
return nil, fmt.Errorf("lob error: initial reader %[1]T %[1]v", v)
}
f.SetLobReader(v.Lob.rd)
return fmt.Sprintf("<lob %d", idx), nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return convertNvLob(idx, f, rv.Elem().Interface())
}
return nil, fmt.Errorf("unsupported lob conversion type error %[1]T %[1]v", v)
}

View File

@ -1,377 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"database/sql/driver"
"errors"
"fmt"
"math"
"math/big"
"sync"
)
//bigint word size (*--> src/pkg/math/big/arith.go)
const (
// Compute the size _S of a Word in bytes.
_m = ^big.Word(0)
_logS = _m>>8&1 + _m>>16&1 + _m>>32&1
_S = 1 << _logS
)
const (
// http://en.wikipedia.org/wiki/Decimal128_floating-point_format
dec128Digits = 34
dec128Bias = 6176
dec128MinExp = -6176
dec128MaxExp = 6111
)
const (
decimalSize = 16 //number of bytes
)
var natZero = big.NewInt(0)
var natOne = big.NewInt(1)
var natTen = big.NewInt(10)
var nat = []*big.Int{
natOne, //10^0
natTen, //10^1
big.NewInt(100), //10^2
big.NewInt(1000), //10^3
big.NewInt(10000), //10^4
big.NewInt(100000), //10^5
big.NewInt(1000000), //10^6
big.NewInt(10000000), //10^7
big.NewInt(100000000), //10^8
big.NewInt(1000000000), //10^9
big.NewInt(10000000000), //10^10
}
const lg10 = math.Ln10 / math.Ln2 // ~log2(10)
var maxDecimal = new(big.Int).SetBytes([]byte{0x01, 0xED, 0x09, 0xBE, 0xAD, 0x87, 0xC0, 0x37, 0x8D, 0x8E, 0x63, 0xFF, 0xFF, 0xFF, 0xFF})
type decFlags byte
const (
dfNotExact decFlags = 1 << iota
dfOverflow
dfUnderflow
)
// ErrDecimalOutOfRange means that a big.Rat exceeds the size of hdb decimal fields.
var ErrDecimalOutOfRange = errors.New("decimal out of range error")
// big.Int free list
var bigIntFree = sync.Pool{
New: func() interface{} { return new(big.Int) },
}
// big.Rat free list
var bigRatFree = sync.Pool{
New: func() interface{} { return new(big.Rat) },
}
// A Decimal is the driver representation of a database decimal field value as big.Rat.
type Decimal big.Rat
// Scan implements the database/sql/Scanner interface.
func (d *Decimal) Scan(src interface{}) error {
b, ok := src.([]byte)
if !ok {
return fmt.Errorf("decimal: invalid data type %T", src)
}
if len(b) != decimalSize {
return fmt.Errorf("decimal: invalid size %d of %v - %d expected", len(b), b, decimalSize)
}
if (b[15] & 0x60) == 0x60 {
return fmt.Errorf("decimal: format (infinity, nan, ...) not supported : %v", b)
}
v := (*big.Rat)(d)
p := v.Num()
q := v.Denom()
neg, exp := decodeDecimal(b, p)
switch {
case exp < 0:
q.Set(exp10(exp * -1))
case exp == 0:
q.Set(natOne)
case exp > 0:
p.Mul(p, exp10(exp))
q.Set(natOne)
}
if neg {
v.Neg(v)
}
return nil
}
// Value implements the database/sql/Valuer interface.
func (d Decimal) Value() (driver.Value, error) {
m := bigIntFree.Get().(*big.Int)
neg, exp, df := convertRatToDecimal((*big.Rat)(&d), m, dec128Digits, dec128MinExp, dec128MaxExp)
var v driver.Value
var err error
switch {
default:
v, err = encodeDecimal(m, neg, exp)
case df&dfUnderflow != 0: // set to zero
m.Set(natZero)
v, err = encodeDecimal(m, false, 0)
case df&dfOverflow != 0:
err = ErrDecimalOutOfRange
}
// performance (avoid expensive defer)
bigIntFree.Put(m)
return v, err
}
func convertRatToDecimal(x *big.Rat, m *big.Int, digits, minExp, maxExp int) (bool, int, decFlags) {
neg := x.Sign() < 0 //store sign
if x.Num().Cmp(natZero) == 0 { // zero
m.Set(natZero)
return neg, 0, 0
}
c := bigRatFree.Get().(*big.Rat).Abs(x) // copy && abs
a := c.Num()
b := c.Denom()
exp, shift := 0, 0
if c.IsInt() {
exp = digits10(a) - 1
} else {
shift = digits10(a) - digits10(b)
switch {
case shift < 0:
a.Mul(a, exp10(shift*-1))
case shift > 0:
b.Mul(b, exp10(shift))
}
if a.Cmp(b) == -1 {
exp = shift - 1
} else {
exp = shift
}
}
var df decFlags
switch {
default:
exp = max(exp-digits+1, minExp)
case exp < minExp:
df |= dfUnderflow
exp = exp - digits + 1
}
if exp > maxExp {
df |= dfOverflow
}
shift = exp - shift
switch {
case shift < 0:
a.Mul(a, exp10(shift*-1))
case exp > 0:
b.Mul(b, exp10(shift))
}
m.QuoRem(a, b, a) // reuse a as rest
if a.Cmp(natZero) != 0 {
// round (business >= 0.5 up)
df |= dfNotExact
if a.Add(a, a).Cmp(b) >= 0 {
m.Add(m, natOne)
if m.Cmp(exp10(digits)) == 0 {
shift := min(digits, maxExp-exp)
if shift < 1 { // overflow -> shift one at minimum
df |= dfOverflow
shift = 1
}
m.Set(exp10(digits - shift))
exp += shift
}
}
}
// norm
for exp < maxExp {
a.QuoRem(m, natTen, b) // reuse a, b
if b.Cmp(natZero) != 0 {
break
}
m.Set(a)
exp++
}
// performance (avoid expensive defer)
bigRatFree.Put(c)
return neg, exp, df
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// performance: tested with reference work variable
// - but int.Set is expensive, so let's live with big.Int creation for n >= len(nat)
func exp10(n int) *big.Int {
if n < len(nat) {
return nat[n]
}
r := big.NewInt(int64(n))
return r.Exp(natTen, r, nil)
}
func digits10(p *big.Int) int {
k := p.BitLen() // 2^k <= p < 2^(k+1) - 1
//i := int(float64(k) / lg10) //minimal digits base 10
//i := int(float64(k) / lg10) //minimal digits base 10
i := k * 100 / 332
if i < 1 {
i = 1
}
for ; ; i++ {
if p.Cmp(exp10(i)) < 0 {
return i
}
}
}
func decodeDecimal(b []byte, m *big.Int) (bool, int) {
neg := (b[15] & 0x80) != 0
exp := int((((uint16(b[15])<<8)|uint16(b[14]))<<1)>>2) - dec128Bias
b14 := b[14] // save b[14]
b[14] &= 0x01 // keep the mantissa bit (rest: sign and exp)
//most significand byte
msb := 14
for msb > 0 {
if b[msb] != 0 {
break
}
msb--
}
//calc number of words
numWords := (msb / _S) + 1
w := make([]big.Word, numWords)
k := numWords - 1
d := big.Word(0)
for i := msb; i >= 0; i-- {
d |= big.Word(b[i])
if k*_S == i {
w[k] = d
k--
d = 0
}
d <<= 8
}
b[14] = b14 // restore b[14]
m.SetBits(w)
return neg, exp
}
func encodeDecimal(m *big.Int, neg bool, exp int) (driver.Value, error) {
b := make([]byte, decimalSize)
// little endian bigint words (significand) -> little endian db decimal format
j := 0
for _, d := range m.Bits() {
for i := 0; i < 8; i++ {
b[j] = byte(d)
d >>= 8
j++
}
}
exp += dec128Bias
b[14] |= (byte(exp) << 1)
b[15] = byte(uint16(exp) >> 7)
if neg {
b[15] |= 0x80
}
return b, nil
}
// NullDecimal represents an Decimal that may be null.
// NullDecimal implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullDecimal struct {
Decimal *Decimal
Valid bool // Valid is true if Decimal is not NULL
}
// Scan implements the Scanner interface.
func (n *NullDecimal) Scan(value interface{}) error {
var b []byte
b, n.Valid = value.([]byte)
if !n.Valid {
return nil
}
if n.Decimal == nil {
return fmt.Errorf("invalid decimal value %v", n.Decimal)
}
return n.Decimal.Scan(b)
}
// Value implements the driver Valuer interface.
func (n NullDecimal) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
if n.Decimal == nil {
return nil, fmt.Errorf("invalid decimal value %v", n.Decimal)
}
return n.Decimal.Value()
}

View File

@ -1,18 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package driver is a native Go SAP HANA driver implementation for the database/sql package.
package driver

View File

@ -1,542 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"io"
"reflect"
"time"
"github.com/SAP/go-hdb/driver/sqltrace"
p "github.com/SAP/go-hdb/internal/protocol"
)
// DriverVersion is the version number of the hdb driver.
const DriverVersion = "0.12.0"
// DriverName is the driver name to use with sql.Open for hdb databases.
const DriverName = "hdb"
// Transaction isolation levels supported by hdb.
const (
LevelReadCommitted = "READ COMMITTED"
LevelRepeatableRead = "REPEATABLE READ"
LevelSerializable = "SERIALIZABLE"
)
// Access modes supported by hdb.
const (
modeReadOnly = "READ ONLY"
modeReadWrite = "READ WRITE"
)
// map sql isolation level to hdb isolation level.
var isolationLevel = map[driver.IsolationLevel]string{
driver.IsolationLevel(sql.LevelDefault): LevelReadCommitted,
driver.IsolationLevel(sql.LevelReadCommitted): LevelReadCommitted,
driver.IsolationLevel(sql.LevelRepeatableRead): LevelRepeatableRead,
driver.IsolationLevel(sql.LevelSerializable): LevelSerializable,
}
// map sql read only flag to hdb access mode.
var readOnly = map[bool]string{
true: modeReadOnly,
false: modeReadWrite,
}
// ErrUnsupportedIsolationLevel is the error raised if a transaction is started with a not supported isolation level.
var ErrUnsupportedIsolationLevel = errors.New("Unsupported isolation level")
// ErrNestedTransaction is the error raised if a tranasction is created within a transaction as this is not supported by hdb.
var ErrNestedTransaction = errors.New("Nested transactions are not supported")
// needed for testing
const driverDataFormatVersion = 1
// queries
const (
pingQuery = "select 1 from dummy"
isolationLevelStmt = "set transaction isolation level %s"
accessModeStmt = "set transaction %s"
)
// bulk statement
const noFlush = "$nf"
// NoFlush is to be used as parameter in bulk inserts.
var NoFlush = sql.Named(noFlush, nil)
var drv = &hdbDrv{}
func init() {
sql.Register(DriverName, drv)
}
// driver
// check if driver implements all required interfaces
var (
_ driver.Driver = (*hdbDrv)(nil)
)
type hdbDrv struct{}
func (d *hdbDrv) Open(dsn string) (driver.Conn, error) {
connector, err := NewDSNConnector(dsn)
if err != nil {
return nil, err
}
return connector.Connect(context.Background())
}
// database connection
// check if conn implements all required interfaces
var (
_ driver.Conn = (*conn)(nil)
_ driver.ConnPrepareContext = (*conn)(nil)
_ driver.Pinger = (*conn)(nil)
_ driver.ConnBeginTx = (*conn)(nil)
_ driver.ExecerContext = (*conn)(nil)
//go 1.9 issue (ExecerContext is only called if Execer is implemented)
_ driver.Execer = (*conn)(nil)
_ driver.QueryerContext = (*conn)(nil)
//go 1.9 issue (QueryerContext is only called if Queryer is implemented)
// QueryContext is needed for stored procedures with table output parameters.
_ driver.Queryer = (*conn)(nil)
_ driver.NamedValueChecker = (*conn)(nil)
)
type conn struct {
session *p.Session
}
func newConn(ctx context.Context, c *Connector) (driver.Conn, error) {
session, err := p.NewSession(ctx, c)
if err != nil {
return nil, err
}
return &conn{session: session}, nil
}
func (c *conn) Prepare(query string) (driver.Stmt, error) {
panic("deprecated")
}
func (c *conn) Close() error {
c.session.Close()
return nil
}
func (c *conn) Begin() (driver.Tx, error) {
panic("deprecated")
}
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
if c.session.InTx() {
return nil, ErrNestedTransaction
}
level, ok := isolationLevel[opts.Isolation]
if !ok {
return nil, ErrUnsupportedIsolationLevel
}
done := make(chan struct{})
go func() {
// set isolation level
if _, err = c.ExecContext(ctx, fmt.Sprintf(isolationLevelStmt, level), nil); err != nil {
goto done
}
// set access mode
if _, err = c.ExecContext(ctx, fmt.Sprintf(accessModeStmt, readOnly[opts.ReadOnly]), nil); err != nil {
goto done
}
c.session.SetInTx(true)
tx = newTx(c.session)
done:
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return tx, err
}
}
// Exec implements the database/sql/driver/Execer interface.
// delete after go 1.9 compatibility is given up.
func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
panic("deprecated")
}
// ExecContext implements the database/sql/driver/ExecerContext interface.
func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
if len(args) != 0 {
return nil, driver.ErrSkip //fast path not possible (prepare needed)
}
sqltrace.Traceln(query)
done := make(chan struct{})
go func() {
r, err = c.session.ExecDirect(query)
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return r, err
}
}
// Queryer implements the database/sql/driver/Queryer interface.
// delete after go 1.9 compatibility is given up.
func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
panic("deprecated")
}
func (c *conn) Ping(ctx context.Context) (err error) {
if c.session.IsBad() {
return driver.ErrBadConn
}
done := make(chan struct{})
go func() {
_, err = c.QueryContext(ctx, pingQuery, nil)
close(done)
}()
select {
case <-ctx.Done():
return ctx.Err()
case <-done:
return err
}
}
// CheckNamedValue implements NamedValueChecker interface.
// implemented for conn:
// if querier or execer is called, sql checks parameters before in case of
// parameters the method can be 'skipped' and force the prepare path
// --> guarantee that a valid driver value is returned
// --> if not implemented, Lob need to have a pseudo Value method to return a valid driver value
func (c *conn) CheckNamedValue(nv *driver.NamedValue) error {
switch nv.Value.(type) {
case Lob, *Lob:
nv.Value = nil
}
return nil
}
//transaction
// check if tx implements all required interfaces
var (
_ driver.Tx = (*tx)(nil)
)
type tx struct {
session *p.Session
}
func newTx(session *p.Session) *tx {
return &tx{
session: session,
}
}
func (t *tx) Commit() error {
if t.session.IsBad() {
return driver.ErrBadConn
}
return t.session.Commit()
}
func (t *tx) Rollback() error {
if t.session.IsBad() {
return driver.ErrBadConn
}
return t.session.Rollback()
}
//statement
// check if stmt implements all required interfaces
var (
_ driver.Stmt = (*stmt)(nil)
_ driver.StmtExecContext = (*stmt)(nil)
_ driver.StmtQueryContext = (*stmt)(nil)
_ driver.NamedValueChecker = (*stmt)(nil)
)
type stmt struct {
qt p.QueryType
session *p.Session
query string
id uint64
prmFieldSet *p.ParameterFieldSet
resultFieldSet *p.ResultFieldSet
}
func newStmt(qt p.QueryType, session *p.Session, query string, id uint64, prmFieldSet *p.ParameterFieldSet, resultFieldSet *p.ResultFieldSet) (*stmt, error) {
return &stmt{qt: qt, session: session, query: query, id: id, prmFieldSet: prmFieldSet, resultFieldSet: resultFieldSet}, nil
}
func (s *stmt) Close() error {
return s.session.DropStatementID(s.id)
}
func (s *stmt) NumInput() int {
return s.prmFieldSet.NumInputField()
}
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
panic("deprecated")
}
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) {
if s.session.IsBad() {
return nil, driver.ErrBadConn
}
numField := s.prmFieldSet.NumInputField()
if len(args) != numField {
return nil, fmt.Errorf("invalid number of arguments %d - %d expected", len(args), numField)
}
sqltrace.Tracef("%s %v", s.query, args)
done := make(chan struct{})
go func() {
r, err = s.session.Exec(s.id, s.prmFieldSet, args)
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return r, err
}
}
func (s *stmt) Query(args []driver.Value) (rows driver.Rows, err error) {
panic("deprecated")
}
// Deprecated: see NamedValueChecker.
//func (s *stmt) ColumnConverter(idx int) driver.ValueConverter {
//}
// CheckNamedValue implements NamedValueChecker interface.
func (s *stmt) CheckNamedValue(nv *driver.NamedValue) error {
if nv.Name == noFlush {
//...
print("remove variable")
return driver.ErrRemoveArgument
}
return checkNamedValue(s.prmFieldSet, nv)
}
// driver.Rows drop-in replacement if driver Query or QueryRow is used for statements that doesn't return rows
var noColumns = []string{}
var noResult = new(noResultType)
// check if noResultType implements all required interfaces
var (
_ driver.Rows = (*noResultType)(nil)
)
type noResultType struct{}
func (r *noResultType) Columns() []string { return noColumns }
func (r *noResultType) Close() error { return nil }
func (r *noResultType) Next(dest []driver.Value) error { return io.EOF }
// rows
type rows struct {
}
// query result
// check if queryResult implements all required interfaces
var (
_ driver.Rows = (*queryResult)(nil)
_ driver.RowsColumnTypeDatabaseTypeName = (*queryResult)(nil) // go 1.8
_ driver.RowsColumnTypeLength = (*queryResult)(nil) // go 1.8
_ driver.RowsColumnTypeNullable = (*queryResult)(nil) // go 1.8
_ driver.RowsColumnTypePrecisionScale = (*queryResult)(nil) // go 1.8
_ driver.RowsColumnTypeScanType = (*queryResult)(nil) // go 1.8
)
type queryResult struct {
session *p.Session
id uint64
resultFieldSet *p.ResultFieldSet
fieldValues *p.FieldValues
pos int
attrs p.PartAttributes
columns []string
lastErr error
}
func newQueryResult(session *p.Session, id uint64, resultFieldSet *p.ResultFieldSet, fieldValues *p.FieldValues, attrs p.PartAttributes) (driver.Rows, error) {
columns := make([]string, resultFieldSet.NumField())
for i := 0; i < len(columns); i++ {
columns[i] = resultFieldSet.Field(i).Name()
}
return &queryResult{
session: session,
id: id,
resultFieldSet: resultFieldSet,
fieldValues: fieldValues,
attrs: attrs,
columns: columns,
}, nil
}
func (r *queryResult) Columns() []string {
return r.columns
}
func (r *queryResult) Close() error {
// if lastError is set, attrs are nil
if r.lastErr != nil {
return r.lastErr
}
if !r.attrs.ResultsetClosed() {
return r.session.CloseResultsetID(r.id)
}
return nil
}
func (r *queryResult) Next(dest []driver.Value) error {
if r.session.IsBad() {
return driver.ErrBadConn
}
if r.pos >= r.fieldValues.NumRow() {
if r.attrs.LastPacket() {
return io.EOF
}
var err error
if r.attrs, err = r.session.FetchNext(r.id, r.resultFieldSet, r.fieldValues); err != nil {
r.lastErr = err //fieldValues and attrs are nil
return err
}
if r.attrs.NoRows() {
return io.EOF
}
r.pos = 0
}
r.fieldValues.Row(r.pos, dest)
r.pos++
return nil
}
func (r *queryResult) ColumnTypeDatabaseTypeName(idx int) string {
return r.resultFieldSet.Field(idx).TypeCode().TypeName()
}
func (r *queryResult) ColumnTypeLength(idx int) (int64, bool) {
return r.resultFieldSet.Field(idx).TypeLength()
}
func (r *queryResult) ColumnTypePrecisionScale(idx int) (int64, int64, bool) {
return r.resultFieldSet.Field(idx).TypePrecisionScale()
}
func (r *queryResult) ColumnTypeNullable(idx int) (bool, bool) {
return r.resultFieldSet.Field(idx).Nullable(), true
}
var (
scanTypeUnknown = reflect.TypeOf(new(interface{})).Elem()
scanTypeTinyint = reflect.TypeOf(uint8(0))
scanTypeSmallint = reflect.TypeOf(int16(0))
scanTypeInteger = reflect.TypeOf(int32(0))
scanTypeBigint = reflect.TypeOf(int64(0))
scanTypeReal = reflect.TypeOf(float32(0.0))
scanTypeDouble = reflect.TypeOf(float64(0.0))
scanTypeTime = reflect.TypeOf(time.Time{})
scanTypeString = reflect.TypeOf(string(""))
scanTypeBytes = reflect.TypeOf([]byte{})
scanTypeDecimal = reflect.TypeOf(Decimal{})
scanTypeLob = reflect.TypeOf(Lob{})
)
func (r *queryResult) ColumnTypeScanType(idx int) reflect.Type {
switch r.resultFieldSet.Field(idx).TypeCode().DataType() {
default:
return scanTypeUnknown
case p.DtTinyint:
return scanTypeTinyint
case p.DtSmallint:
return scanTypeSmallint
case p.DtInteger:
return scanTypeInteger
case p.DtBigint:
return scanTypeBigint
case p.DtReal:
return scanTypeReal
case p.DtDouble:
return scanTypeDouble
case p.DtTime:
return scanTypeTime
case p.DtDecimal:
return scanTypeDecimal
case p.DtString:
return scanTypeString
case p.DtBytes:
return scanTypeBytes
case p.DtLob:
return scanTypeLob
}
}

View File

@ -1,162 +0,0 @@
// +build future
/*
Copyright 2018 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"context"
//"database/sql"
"database/sql/driver"
"github.com/SAP/go-hdb/driver/sqltrace"
p "github.com/SAP/go-hdb/internal/protocol"
)
// database connection
func (c *conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
done := make(chan struct{})
go func() {
var (
qt p.QueryType
id uint64
prmFieldSet *p.ParameterFieldSet
resultFieldSet *p.ResultFieldSet
)
qt, id, prmFieldSet, resultFieldSet, err = c.session.Prepare(query)
if err != nil {
goto done
}
select {
default:
case <-ctx.Done():
return
}
stmt, err = newStmt(qt, c.session, query, id, prmFieldSet, resultFieldSet)
done:
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return stmt, err
}
}
// QueryContext implements the database/sql/driver/QueryerContext interface.
func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
if len(args) != 0 {
return nil, driver.ErrSkip //fast path not possible (prepare needed)
}
// direct execution of call procedure
// - returns no parameter metadata (sps 82) but only field values
// --> let's take the 'prepare way' for stored procedures
// if checkCallProcedure(query) {
// return nil, driver.ErrSkip
// }
sqltrace.Traceln(query)
done := make(chan struct{})
go func() {
var (
id uint64
resultFieldSet *p.ResultFieldSet
fieldValues *p.FieldValues
attributes p.PartAttributes
)
id, resultFieldSet, fieldValues, attributes, err = c.session.QueryDirect(query)
if err != nil {
goto done
}
select {
default:
case <-ctx.Done():
return
}
if id == 0 { // non select query
rows = noResult
} else {
rows, err = newQueryResult(c.session, id, resultFieldSet, fieldValues, attributes)
}
done:
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return rows, err
}
}
//statement
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) {
if s.session.IsBad() {
return nil, driver.ErrBadConn
}
done := make(chan struct{})
go func() {
rows, err = s.defaultQuery(ctx, args)
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return rows, err
}
}
func (s *stmt) defaultQuery(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
sqltrace.Tracef("%s %v", s.query, args)
rid, values, attributes, err := s.session.Query(s.id, s.prmFieldSet, s.resultFieldSet, args)
if err != nil {
return nil, err
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
if rid == 0 { // non select query
return noResult, nil
}
return newQueryResult(s.session, rid, s.resultFieldSet, values, attributes)
}

View File

@ -1,32 +0,0 @@
// +build go1.10
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"database/sql/driver"
)
// driver
// check if driver implements all required interfaces
var _ driver.DriverContext = (*hdbDrv)(nil)
func (d *hdbDrv) OpenConnector(dsn string) (driver.Connector, error) {
return NewDSNConnector(dsn)
}

View File

@ -1,507 +0,0 @@
// +build !future
/*
Copyright 2018 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"context"
"database/sql/driver"
"encoding/binary"
"fmt"
"io"
"regexp"
"sync"
"github.com/SAP/go-hdb/driver/sqltrace"
p "github.com/SAP/go-hdb/internal/protocol"
)
var reBulk = regexp.MustCompile("(?i)^(\\s)*(bulk +)(.*)")
func checkBulkInsert(sql string) (string, bool) {
if reBulk.MatchString(sql) {
return reBulk.ReplaceAllString(sql, "${3}"), true
}
return sql, false
}
var reCall = regexp.MustCompile("(?i)^(\\s)*(call +)(.*)")
func checkCallProcedure(sql string) bool {
return reCall.MatchString(sql)
}
// database connection
func (c *conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
done := make(chan struct{})
go func() {
prepareQuery, bulkInsert := checkBulkInsert(query)
var (
qt p.QueryType
id uint64
prmFieldSet *p.ParameterFieldSet
resultFieldSet *p.ResultFieldSet
)
qt, id, prmFieldSet, resultFieldSet, err = c.session.Prepare(prepareQuery)
if err != nil {
goto done
}
select {
default:
case <-ctx.Done():
return
}
if bulkInsert {
stmt, err = newBulkInsertStmt(c.session, prepareQuery, id, prmFieldSet)
} else {
stmt, err = newStmt(qt, c.session, prepareQuery, id, prmFieldSet, resultFieldSet)
}
done:
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return stmt, err
}
}
// QueryContext implements the database/sql/driver/QueryerContext interface.
func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) {
if c.session.IsBad() {
return nil, driver.ErrBadConn
}
if len(args) != 0 {
return nil, driver.ErrSkip //fast path not possible (prepare needed)
}
// direct execution of call procedure
// - returns no parameter metadata (sps 82) but only field values
// --> let's take the 'prepare way' for stored procedures
if checkCallProcedure(query) {
return nil, driver.ErrSkip
}
sqltrace.Traceln(query)
id, idx, ok := decodeTableQuery(query)
if ok {
r := procedureCallResultStore.get(id)
if r == nil {
return nil, fmt.Errorf("invalid procedure table query %s", query)
}
return r.tableRows(int(idx))
}
done := make(chan struct{})
go func() {
var (
id uint64
resultFieldSet *p.ResultFieldSet
fieldValues *p.FieldValues
attributes p.PartAttributes
)
id, resultFieldSet, fieldValues, attributes, err = c.session.QueryDirect(query)
if err != nil {
goto done
}
select {
default:
case <-ctx.Done():
return
}
if id == 0 { // non select query
rows = noResult
} else {
rows, err = newQueryResult(c.session, id, resultFieldSet, fieldValues, attributes)
}
done:
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return rows, err
}
}
//statement
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) {
if s.session.IsBad() {
return nil, driver.ErrBadConn
}
done := make(chan struct{})
go func() {
switch s.qt {
default:
rows, err = s.defaultQuery(ctx, args)
case p.QtProcedureCall:
rows, err = s.procedureCall(ctx, args)
}
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return rows, err
}
}
func (s *stmt) defaultQuery(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
sqltrace.Tracef("%s %v", s.query, args)
rid, values, attributes, err := s.session.Query(s.id, s.prmFieldSet, s.resultFieldSet, args)
if err != nil {
return nil, err
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
if rid == 0 { // non select query
return noResult, nil
}
return newQueryResult(s.session, rid, s.resultFieldSet, values, attributes)
}
func (s *stmt) procedureCall(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
sqltrace.Tracef("%s %v", s.query, args)
fieldValues, tableResults, err := s.session.Call(s.id, s.prmFieldSet, args)
if err != nil {
return nil, err
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
return newProcedureCallResult(s.session, s.prmFieldSet, fieldValues, tableResults)
}
// bulk insert statement
// check if bulkInsertStmt implements all required interfaces
var (
_ driver.Stmt = (*bulkInsertStmt)(nil)
_ driver.StmtExecContext = (*bulkInsertStmt)(nil)
_ driver.StmtQueryContext = (*bulkInsertStmt)(nil)
_ driver.NamedValueChecker = (*bulkInsertStmt)(nil)
)
type bulkInsertStmt struct {
session *p.Session
query string
id uint64
prmFieldSet *p.ParameterFieldSet
numArg int
args []driver.NamedValue
}
func newBulkInsertStmt(session *p.Session, query string, id uint64, prmFieldSet *p.ParameterFieldSet) (*bulkInsertStmt, error) {
return &bulkInsertStmt{session: session, query: query, id: id, prmFieldSet: prmFieldSet, args: make([]driver.NamedValue, 0)}, nil
}
func (s *bulkInsertStmt) Close() error {
return s.session.DropStatementID(s.id)
}
func (s *bulkInsertStmt) NumInput() int {
return -1
}
func (s *bulkInsertStmt) Exec(args []driver.Value) (driver.Result, error) {
panic("deprecated")
}
func (s *bulkInsertStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) {
if s.session.IsBad() {
return nil, driver.ErrBadConn
}
sqltrace.Tracef("%s %v", s.query, args)
done := make(chan struct{})
go func() {
if args == nil || len(args) == 0 {
r, err = s.execFlush()
} else {
r, err = s.execBuffer(args)
}
close(done)
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-done:
return r, err
}
}
func (s *bulkInsertStmt) execFlush() (driver.Result, error) {
if s.numArg == 0 {
return driver.ResultNoRows, nil
}
sqltrace.Traceln("execFlush")
result, err := s.session.Exec(s.id, s.prmFieldSet, s.args)
s.args = s.args[:0]
s.numArg = 0
return result, err
}
func (s *bulkInsertStmt) execBuffer(args []driver.NamedValue) (driver.Result, error) {
numField := s.prmFieldSet.NumInputField()
if len(args) != numField {
return nil, fmt.Errorf("invalid number of arguments %d - %d expected", len(args), numField)
}
var result driver.Result = driver.ResultNoRows
var err error
if s.numArg == maxSmallint { // TODO: check why bigArgument count does not work
result, err = s.execFlush()
}
s.args = append(s.args, args...)
s.numArg++
return result, err
}
func (s *bulkInsertStmt) Query(args []driver.Value) (driver.Rows, error) {
panic("deprecated")
}
func (s *bulkInsertStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
return nil, fmt.Errorf("query not allowed in context of bulk insert statement %s", s.query)
}
// Deprecated: see NamedValueChecker.
//func (s *bulkInsertStmt) ColumnConverter(idx int) driver.ValueConverter {
//}
// CheckNamedValue implements NamedValueChecker interface.
func (s *bulkInsertStmt) CheckNamedValue(nv *driver.NamedValue) error {
return checkNamedValue(s.prmFieldSet, nv)
}
//call result store
type callResultStore struct {
mu sync.RWMutex
store map[uint64]*procedureCallResult
cnt uint64
free []uint64
}
func (s *callResultStore) get(k uint64) *procedureCallResult {
s.mu.RLock()
defer s.mu.RUnlock()
if r, ok := s.store[k]; ok {
return r
}
return nil
}
func (s *callResultStore) add(v *procedureCallResult) uint64 {
s.mu.Lock()
defer s.mu.Unlock()
var k uint64
if s.free == nil || len(s.free) == 0 {
s.cnt++
k = s.cnt
} else {
size := len(s.free)
k = s.free[size-1]
s.free = s.free[:size-1]
}
if s.store == nil {
s.store = make(map[uint64]*procedureCallResult)
}
s.store[k] = v
return k
}
func (s *callResultStore) del(k uint64) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.store, k)
if s.free == nil {
s.free = []uint64{k}
} else {
s.free = append(s.free, k)
}
}
var procedureCallResultStore = new(callResultStore)
//procedure call result
// check if procedureCallResult implements all required interfaces
var _ driver.Rows = (*procedureCallResult)(nil)
type procedureCallResult struct {
id uint64
session *p.Session
prmFieldSet *p.ParameterFieldSet
fieldValues *p.FieldValues
_tableRows []driver.Rows
columns []string
eof error
}
func newProcedureCallResult(session *p.Session, prmFieldSet *p.ParameterFieldSet, fieldValues *p.FieldValues, tableResults []*p.TableResult) (driver.Rows, error) {
fieldIdx := prmFieldSet.NumOutputField()
columns := make([]string, fieldIdx+len(tableResults))
for i := 0; i < fieldIdx; i++ {
columns[i] = prmFieldSet.OutputField(i).Name()
}
tableRows := make([]driver.Rows, len(tableResults))
for i, tableResult := range tableResults {
var err error
if tableRows[i], err = newQueryResult(session, tableResult.ID(), tableResult.FieldSet(), tableResult.FieldValues(), tableResult.Attrs()); err != nil {
return nil, err
}
columns[fieldIdx] = fmt.Sprintf("table %d", i)
fieldIdx++
}
result := &procedureCallResult{
session: session,
prmFieldSet: prmFieldSet,
fieldValues: fieldValues,
_tableRows: tableRows,
columns: columns,
}
id := procedureCallResultStore.add(result)
result.id = id
return result, nil
}
func (r *procedureCallResult) Columns() []string {
return r.columns
}
func (r *procedureCallResult) Close() error {
procedureCallResultStore.del(r.id)
return nil
}
func (r *procedureCallResult) Next(dest []driver.Value) error {
if r.session.IsBad() {
return driver.ErrBadConn
}
if r.eof != nil {
return r.eof
}
if r.fieldValues.NumRow() == 0 && len(r._tableRows) == 0 {
r.eof = io.EOF
return r.eof
}
if r.fieldValues.NumRow() != 0 {
r.fieldValues.Row(0, dest)
}
i := r.prmFieldSet.NumOutputField()
for j := range r._tableRows {
dest[i] = encodeTableQuery(r.id, uint64(j))
i++
}
r.eof = io.EOF
return nil
}
func (r *procedureCallResult) tableRows(idx int) (driver.Rows, error) {
if idx >= len(r._tableRows) {
return nil, fmt.Errorf("table row index %d exceeds maximun %d", idx, len(r._tableRows)-1)
}
return r._tableRows[idx], nil
}
// helper
const tableQueryPrefix = "@tq"
func encodeTableQuery(id, idx uint64) string {
start := len(tableQueryPrefix)
b := make([]byte, start+8+8)
copy(b, tableQueryPrefix)
binary.LittleEndian.PutUint64(b[start:start+8], id)
binary.LittleEndian.PutUint64(b[start+8:start+8+8], idx)
return string(b)
}
func decodeTableQuery(query string) (uint64, uint64, bool) {
size := len(query)
start := len(tableQueryPrefix)
if size != start+8+8 {
return 0, 0, false
}
if query[:start] != tableQueryPrefix {
return 0, 0, false
}
id := binary.LittleEndian.Uint64([]byte(query[start : start+8]))
idx := binary.LittleEndian.Uint64([]byte(query[start+8 : start+8+8]))
return id, idx, true
}

View File

@ -1,64 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
// DSN parameters. For parameter client locale see http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf.
const (
DSNLocale = "locale" // Client locale as described in the protocol reference.
DSNTimeout = "timeout" // Driver side connection timeout in seconds.
DSNFetchSize = "fetchSize" // Maximum number of fetched records from database by database/sql/driver/Rows.Next().
)
/*
DSN TLS parameters.
For more information please see https://golang.org/pkg/crypto/tls/#Config.
For more flexibility in TLS configuration please see driver.Connector.
*/
const (
DSNTLSRootCAFile = "TLSRootCAFile" // Path,- filename to root certificate(s).
DSNTLSServerName = "TLSServerName" // ServerName to verify the hostname.
DSNTLSInsecureSkipVerify = "TLSInsecureSkipVerify" // Controls whether a client verifies the server's certificate chain and host name.
)
// DSN default values.
const (
DefaultTimeout = 300 // Default value connection timeout (300 seconds = 5 minutes).
DefaultFetchSize = 128 // Default value fetchSize.
)
// DSN minimal values.
const (
minTimeout = 0 // Minimal timeout value.
minFetchSize = 1 // Minimal fetchSize value.
)
/*
DSN is here for the purposes of documentation only. A DSN string is an URL string with the following format
"hdb://<username>:<password>@<host address>:<port number>"
and optional query parameters (see DSN query parameters and DSN query default values).
Example:
"hdb://myuser:mypassword@localhost:30015?timeout=60"
Examples TLS connection:
"hdb://myuser:mypassword@localhost:39013?TLSRootCAFile=trust.pem"
"hdb://myuser:mypassword@localhost:39013?TLSRootCAFile=trust.pem&TLSServerName=hostname"
"hdb://myuser:mypassword@localhost:39013?TLSInsecureSkipVerify"
*/
type DSN string

View File

@ -1,39 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
// HDB error levels.
const (
HdbWarning = 0
HdbError = 1
HdbFatalError = 2
)
// Error represents errors send by the database server.
type Error interface {
Error() string // Implements the golang error interface.
NumError() int // NumError returns the number of errors.
SetIdx(idx int) // Sets the error index in case number of errors are greater 1 in the range of 0 <= index < NumError().
StmtNo() int // Returns the statement number of the error in multi statement contexts (e.g. bulk insert).
Code() int // Code return the database error code.
Position() int // Position returns the start position of erroneous sql statements sent to the database server.
Level() int // Level return one of the database server predefined error levels.
Text() string // Text return the error description sent from database server.
IsWarning() bool // IsWarning returns true if the HDB error level equals 0.
IsError() bool // IsError returns true if the HDB error level equals 1.
IsFatal() bool // IsFatal returns true if the HDB error level equals 2.
}

View File

@ -1,49 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"crypto/rand"
"fmt"
"io"
"regexp"
"strconv"
)
var reSimple = regexp.MustCompile("^[_A-Z][_#$A-Z0-9]*$")
// Identifier in hdb SQL statements like schema or table name.
type Identifier string
// RandomIdentifier returns a random Identifier prefixed by the prefix parameter.
// This function is used to generate database objects with random names for test and example code.
func RandomIdentifier(prefix string) Identifier {
b := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
panic(err.Error()) // rand should never fail
}
return Identifier(fmt.Sprintf("%s%x", prefix, b))
}
// String implements Stringer interface.
func (i Identifier) String() string {
s := string(i)
if reSimple.MatchString(s) {
return s
}
return strconv.Quote(s)
}

View File

@ -1,94 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"fmt"
"io"
)
// A Lob is the driver representation of a database large object field.
// A Lob object uses an io.Reader object as source for writing content to a database lob field.
// A Lob object uses an io.Writer object as destination for reading content from a database lob field.
// A Lob can be created by contructor method NewLob with io.Reader and io.Writer as parameters or
// created by new, setting io.Reader and io.Writer by SetReader and SetWriter methods.
type Lob struct {
rd io.Reader
wr io.Writer
}
// NewLob creates a new Lob instance with the io.Reader and io.Writer given as parameters.
func NewLob(rd io.Reader, wr io.Writer) *Lob {
return &Lob{rd: rd, wr: wr}
}
// SetReader sets the io.Reader source for a lob field to be written to database
// and return *Lob, to enable simple call chaining.
func (l *Lob) SetReader(rd io.Reader) *Lob {
l.rd = rd
return l
}
// SetWriter sets the io.Writer destination for a lob field to be read from database
// and return *Lob, to enable simple call chaining.
func (l *Lob) SetWriter(wr io.Writer) *Lob {
l.wr = wr
return l
}
type writerSetter interface {
SetWriter(w io.Writer) error
}
// Scan implements the database/sql/Scanner interface.
func (l *Lob) Scan(src interface{}) error {
if l.wr == nil {
return fmt.Errorf("lob error: initial reader %[1]T %[1]v", l)
}
ws, ok := src.(writerSetter)
if !ok {
return fmt.Errorf("lob: invalid scan type %T", src)
}
if err := ws.SetWriter(l.wr); err != nil {
return err
}
return nil
}
// NullLob represents an Lob that may be null.
// NullLob implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullLob struct {
Lob *Lob
Valid bool // Valid is true if Lob is not NULL
}
// Scan implements the database/sql/Scanner interface.
func (l *NullLob) Scan(src interface{}) error {
if src == nil {
l.Valid = false
return nil
}
if err := l.Lob.Scan(src); err != nil {
return err
}
l.Valid = true
return nil
}

View File

@ -1,18 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package sqltrace implements driver sql trace functions.
package sqltrace

View File

@ -1,78 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sqltrace
import (
"flag"
"log"
"os"
"sync"
)
type sqlTrace struct {
mu sync.RWMutex //protects field on
on bool
*log.Logger
}
func newSQLTrace() *sqlTrace {
return &sqlTrace{
Logger: log.New(os.Stdout, "hdb ", log.Ldate|log.Ltime|log.Lshortfile),
}
}
var tracer = newSQLTrace()
func init() {
flag.BoolVar(&tracer.on, "hdb.sqlTrace", false, "enabling hdb sql trace")
}
// On returns if tracing methods output is active.
func On() bool {
tracer.mu.RLock()
on := tracer.on
tracer.mu.RUnlock()
return on
}
// SetOn sets tracing methods output active or inactive.
func SetOn(on bool) {
tracer.mu.Lock()
tracer.on = on
tracer.mu.Unlock()
}
// Trace calls trace logger Print method to print to the trace logger.
func Trace(v ...interface{}) {
if On() {
tracer.Print(v...)
}
}
// Tracef calls trace logger Printf method to print to the trace logger.
func Tracef(format string, v ...interface{}) {
if On() {
tracer.Printf(format, v...)
}
}
// Traceln calls trace logger Println method to print to the trace logger.
func Traceln(v ...interface{}) {
if On() {
tracer.Println(v...)
}
}

View File

@ -1,44 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package driver
import (
"database/sql/driver"
"time"
)
// NullTime represents an time.Time that may be null.
// NullTime implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
type NullTime struct {
Time time.Time
Valid bool // Valid is true if Time is not NULL
}
// Scan implements the Scanner interface.
func (n *NullTime) Scan(value interface{}) error {
n.Time, n.Valid = value.(time.Time)
return nil
}
// Value implements the driver Valuer interface.
func (n NullTime) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Time, nil
}

View File

@ -1,414 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package bufio implements buffered I/O for database read and writes on basis of the standard Go bufio package.
package bufio
import (
"bufio"
"encoding/binary"
"io"
"math"
"github.com/SAP/go-hdb/internal/unicode"
"golang.org/x/text/transform"
)
// Reader is a bufio.Reader extended by methods needed for hdb protocol.
type Reader struct {
rd *bufio.Reader
err error
b [8]byte // scratch buffer (8 Bytes)
tr transform.Transformer
}
// NewReader creates a new Reader instance.
func NewReader(r io.Reader) *Reader {
return &Reader{
rd: bufio.NewReader(r),
tr: unicode.Cesu8ToUtf8Transformer,
}
}
// NewReaderSize creates a new Reader instance with given size for bufio.Reader.
func NewReaderSize(r io.Reader, size int) *Reader {
return &Reader{
rd: bufio.NewReaderSize(r, size),
tr: unicode.Cesu8ToUtf8Transformer,
}
}
// GetError returns reader error
func (r *Reader) GetError() error {
err := r.err
r.err = nil
return err
}
// Skip skips cnt bytes from reading.
func (r *Reader) Skip(cnt int) {
if r.err != nil {
return
}
_, r.err = r.rd.Discard(cnt)
}
// ReadB reads and returns a byte.
func (r *Reader) ReadB() byte { // ReadB as sig differs from ReadByte (vet issues)
if r.err != nil {
return 0
}
var b byte
b, r.err = r.rd.ReadByte()
return b
}
// ReadFull implements io.ReadFull on Reader.
func (r *Reader) ReadFull(p []byte) {
if r.err != nil {
return
}
_, r.err = io.ReadFull(r.rd, p)
}
// ReadBool reads and returns a boolean.
func (r *Reader) ReadBool() bool {
if r.err != nil {
return false
}
return !(r.ReadB() == 0)
}
// ReadInt8 reads and returns an int8.
func (r *Reader) ReadInt8() int8 {
return int8(r.ReadB())
}
// ReadInt16 reads and returns an int16.
func (r *Reader) ReadInt16() int16 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:2]); r.err != nil {
return 0
}
return int16(binary.LittleEndian.Uint16(r.b[:2]))
}
// ReadUint16 reads and returns an uint16.
func (r *Reader) ReadUint16() uint16 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:2]); r.err != nil {
return 0
}
return binary.LittleEndian.Uint16(r.b[:2])
}
// ReadInt32 reads and returns an int32.
func (r *Reader) ReadInt32() int32 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:4]); r.err != nil {
return 0
}
return int32(binary.LittleEndian.Uint32(r.b[:4]))
}
// ReadUint32 reads and returns an uint32.
func (r *Reader) ReadUint32() uint32 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:4]); r.err != nil {
return 0
}
return binary.LittleEndian.Uint32(r.b[:4])
}
// ReadInt64 reads and returns an int64.
func (r *Reader) ReadInt64() int64 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:8]); r.err != nil {
return 0
}
return int64(binary.LittleEndian.Uint64(r.b[:8]))
}
// ReadUint64 reads and returns an uint64.
func (r *Reader) ReadUint64() uint64 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:8]); r.err != nil {
return 0
}
return binary.LittleEndian.Uint64(r.b[:8])
}
// ReadFloat32 reads and returns a float32.
func (r *Reader) ReadFloat32() float32 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:4]); r.err != nil {
return 0
}
bits := binary.LittleEndian.Uint32(r.b[:4])
return math.Float32frombits(bits)
}
// ReadFloat64 reads and returns a float64.
func (r *Reader) ReadFloat64() float64 {
if r.err != nil {
return 0
}
if _, r.err = io.ReadFull(r.rd, r.b[:8]); r.err != nil {
return 0
}
bits := binary.LittleEndian.Uint64(r.b[:8])
return math.Float64frombits(bits)
}
// ReadCesu8 reads a size CESU-8 encoded byte sequence and returns an UTF-8 byte slice.
func (r *Reader) ReadCesu8(size int) []byte {
if r.err != nil {
return nil
}
p := make([]byte, size)
if _, r.err = io.ReadFull(r.rd, p); r.err != nil {
return nil
}
r.tr.Reset()
var n int
if n, _, r.err = r.tr.Transform(p, p, true); r.err != nil { // inplace transformation
return nil
}
return p[:n]
}
const writerBufferSize = 4096
// Writer is a bufio.Writer extended by methods needed for hdb protocol.
type Writer struct {
wr *bufio.Writer
err error
b []byte // scratch buffer (min 8 Bytes)
tr transform.Transformer
}
// NewWriter creates a new Writer instance.
func NewWriter(w io.Writer) *Writer {
return &Writer{
wr: bufio.NewWriter(w),
b: make([]byte, writerBufferSize),
tr: unicode.Utf8ToCesu8Transformer,
}
}
// NewWriterSize creates a new Writer instance with given size for bufio.Writer.
func NewWriterSize(w io.Writer, size int) *Writer {
return &Writer{
wr: bufio.NewWriterSize(w, size),
b: make([]byte, writerBufferSize),
tr: unicode.Utf8ToCesu8Transformer,
}
}
// Flush writes any buffered data to the underlying io.Writer.
func (w *Writer) Flush() error {
if w.err != nil {
return w.err
}
return w.wr.Flush()
}
// WriteZeroes writes cnt zero byte values.
func (w *Writer) WriteZeroes(cnt int) {
if w.err != nil {
return
}
// zero out scratch area
l := cnt
if l > len(w.b) {
l = len(w.b)
}
for i := 0; i < l; i++ {
w.b[i] = 0
}
for i := 0; i < cnt; {
j := cnt - i
if j > len(w.b) {
j = len(w.b)
}
n, _ := w.wr.Write(w.b[:j])
i += n
}
}
// Write writes the contents of p.
func (w *Writer) Write(p []byte) {
if w.err != nil {
return
}
w.wr.Write(p)
}
// WriteB writes a byte.
func (w *Writer) WriteB(b byte) { // WriteB as sig differs from WriteByte (vet issues)
if w.err != nil {
return
}
w.wr.WriteByte(b)
}
// WriteBool writes a boolean.
func (w *Writer) WriteBool(v bool) {
if w.err != nil {
return
}
if v {
w.wr.WriteByte(1)
} else {
w.wr.WriteByte(0)
}
}
// WriteInt8 writes an int8.
func (w *Writer) WriteInt8(i int8) {
if w.err != nil {
return
}
w.wr.WriteByte(byte(i))
}
// WriteInt16 writes an int16.
func (w *Writer) WriteInt16(i int16) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint16(w.b[:2], uint16(i))
w.wr.Write(w.b[:2])
}
// WriteUint16 writes an uint16.
func (w *Writer) WriteUint16(i uint16) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint16(w.b[:2], i)
w.wr.Write(w.b[:2])
}
// WriteInt32 writes an int32.
func (w *Writer) WriteInt32(i int32) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint32(w.b[:4], uint32(i))
w.wr.Write(w.b[:4])
}
// WriteUint32 writes an uint32.
func (w *Writer) WriteUint32(i uint32) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint32(w.b[:4], i)
w.wr.Write(w.b[:4])
}
// WriteInt64 writes an int64.
func (w *Writer) WriteInt64(i int64) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint64(w.b[:8], uint64(i))
w.wr.Write(w.b[:8])
}
// WriteUint64 writes an uint64.
func (w *Writer) WriteUint64(i uint64) {
if w.err != nil {
return
}
binary.LittleEndian.PutUint64(w.b[:8], i)
w.wr.Write(w.b[:8])
}
// WriteFloat32 writes a float32.
func (w *Writer) WriteFloat32(f float32) {
if w.err != nil {
return
}
bits := math.Float32bits(f)
binary.LittleEndian.PutUint32(w.b[:4], bits)
w.wr.Write(w.b[:4])
}
// WriteFloat64 writes a float64.
func (w *Writer) WriteFloat64(f float64) {
if w.err != nil {
return
}
bits := math.Float64bits(f)
binary.LittleEndian.PutUint64(w.b[:8], bits)
w.wr.Write(w.b[:8])
}
// WriteString writes a string.
func (w *Writer) WriteString(s string) {
if w.err != nil {
return
}
w.wr.WriteString(s)
}
// WriteCesu8 writes an UTF-8 byte slice as CESU-8 and returns the CESU-8 bytes written.
func (w *Writer) WriteCesu8(p []byte) int {
if w.err != nil {
return 0
}
w.tr.Reset()
cnt := 0
i := 0
for i < len(p) {
m, n, err := w.tr.Transform(w.b, p[i:], true)
if err != nil && err != transform.ErrShortDst {
w.err = err
return cnt
}
if m == 0 {
w.err = transform.ErrShortDst
return cnt
}
o, _ := w.wr.Write(w.b[:m])
cnt += o
i += n
}
return cnt
}
// WriteStringCesu8 is like WriteCesu8 with an UTF-8 string as parameter.
func (w *Writer) WriteStringCesu8(s string) int {
return w.WriteCesu8([]byte(s))
}

View File

@ -1,55 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"os"
"strconv"
"strings"
"github.com/SAP/go-hdb/internal/bufio"
)
type clientID []byte
func newClientID() clientID {
if h, err := os.Hostname(); err == nil {
return clientID(strings.Join([]string{strconv.Itoa(os.Getpid()), h}, "@"))
}
return clientID(strconv.Itoa(os.Getpid()))
}
func (id clientID) kind() partKind {
return partKind(pkClientID)
}
func (id clientID) size() (int, error) {
return len(id), nil
}
func (id clientID) numArg() int {
return 1
}
func (id clientID) write(wr *bufio.Writer) error {
wr.Write(id)
if trace {
outLogger.Printf("client id: %s", id)
}
return nil
}

View File

@ -1,47 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"github.com/SAP/go-hdb/internal/bufio"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
)
// cesu8 command
type command []byte
func (c command) kind() partKind {
return pkCommand
}
func (c command) size() (int, error) {
return cesu8.Size(c), nil
}
func (c command) numArg() int {
return 1
}
func (c command) write(wr *bufio.Writer) error {
wr.WriteCesu8(c)
if trace {
outLogger.Printf("command: %s", c)
}
return nil
}

View File

@ -1,57 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=connectOption
type connectOption int8
const (
coConnectionID connectOption = 1
coCompleteArrayExecution connectOption = 2
coClientLocale connectOption = 3
coSupportsLargeBulkOperations connectOption = 4
// duplicate in docu: coDataFormatVersion2 connectOption = 5
// 6-9 reserved: do not use
coLargeNumberOfParameterSupport connectOption = 10
coSystemID connectOption = 11
// 12 reserved: do not use
coAbapVarcharMode connectOption = 13
coSelectForUpdateSupported connectOption = 14
coClientDistributionMode connectOption = 15
coEngineDataFormatVersion connectOption = 16
coDistributionProtocolVersion connectOption = 17
coSplitBatchCommands connectOption = 18
coUseTransactionFlagsOnly connectOption = 19
//coRowAndColumnOptimizedFormat connectOption = 20 reserved: do not use
coIgnoreUnknownParts connectOption = 21
coTableOutputParameter connectOption = 22
coDataFormatVersion2 connectOption = 23
coItabParameter connectOption = 24
coDescribeTableOutputParameter connectOption = 25
coColumnarResultset connectOption = 26
coScrollablResultSet connectOption = 27
coClientInfoNullValueSupported connectOption = 28
coAssociatedConnectionID connectOption = 29
coNoTransactionalPrepare connectOption = 30
coFDAEnabled connectOption = 31
coOSUser connectOption = 32
coRowslotImageResult connectOption = 33
coEndianess connectOption = 34
// 35, 36 reserved: do not use
coImplicitLobStreaming connectOption = 37
)

View File

@ -1,41 +0,0 @@
// Code generated by "stringer -type=connectOption"; DO NOT EDIT.
package protocol
import "strconv"
const (
_connectOption_name_0 = "coConnectionIDcoCompleteArrayExecutioncoClientLocalecoSupportsLargeBulkOperations"
_connectOption_name_1 = "coLargeNumberOfParameterSupportcoSystemID"
_connectOption_name_2 = "coAbapVarcharModecoSelectForUpdateSupportedcoClientDistributionModecoEngineDataFormatVersioncoDistributionProtocolVersioncoSplitBatchCommandscoUseTransactionFlagsOnly"
_connectOption_name_3 = "coIgnoreUnknownPartscoTableOutputParametercoDataFormatVersion2coItabParametercoDescribeTableOutputParametercoColumnarResultsetcoScrollablResultSetcoClientInfoNullValueSupportedcoAssociatedConnectionIDcoNoTransactionalPreparecoFDAEnabledcoOSUsercoRowslotImageResultcoEndianess"
_connectOption_name_4 = "coImplicitLobStreaming"
)
var (
_connectOption_index_0 = [...]uint8{0, 14, 38, 52, 81}
_connectOption_index_1 = [...]uint8{0, 31, 41}
_connectOption_index_2 = [...]uint8{0, 17, 43, 67, 92, 121, 141, 166}
_connectOption_index_3 = [...]uint16{0, 20, 42, 62, 77, 107, 126, 146, 176, 200, 224, 236, 244, 264, 275}
)
func (i connectOption) String() string {
switch {
case 1 <= i && i <= 4:
i -= 1
return _connectOption_name_0[_connectOption_index_0[i]:_connectOption_index_0[i+1]]
case 10 <= i && i <= 11:
i -= 10
return _connectOption_name_1[_connectOption_index_1[i]:_connectOption_index_1[i+1]]
case 13 <= i && i <= 19:
i -= 13
return _connectOption_name_2[_connectOption_index_2[i]:_connectOption_index_2[i+1]]
case 21 <= i && i <= 34:
i -= 21
return _connectOption_name_3[_connectOption_index_3[i]:_connectOption_index_3[i+1]]
case i == 37:
return _connectOption_name_4
default:
return "connectOption(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@ -1,109 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
// data format version
const (
dfvBaseline intType = 1
dfvDoNotUse intType = 3
dfvSPS06 intType = 4 //see docu
dfvBINTEXT intType = 6
)
// client distribution mode
const (
cdmOff intType = 0
cdmConnection = 1
cdmStatement = 2
cdmConnectionStatement = 3
)
// distribution protocol version
const (
dpvBaseline = 0
dpvClientHandlesStatementSequence = 1
)
type connectOptions struct {
po plainOptions
_numArg int
}
func newConnectOptions() *connectOptions {
return &connectOptions{
po: plainOptions{},
}
}
func (o *connectOptions) String() string {
m := make(map[connectOption]interface{})
for k, v := range o.po {
m[connectOption(k)] = v
}
return fmt.Sprintf("%s", m)
}
func (o *connectOptions) kind() partKind {
return pkConnectOptions
}
func (o *connectOptions) size() (int, error) {
return o.po.size(), nil
}
func (o *connectOptions) numArg() int {
return len(o.po)
}
func (o *connectOptions) setNumArg(numArg int) {
o._numArg = numArg
}
func (o *connectOptions) set(k connectOption, v interface{}) {
o.po[int8(k)] = v
}
func (o *connectOptions) get(k connectOption) (interface{}, bool) {
v, ok := o.po[int8(k)]
return v, ok
}
func (o *connectOptions) read(rd *bufio.Reader) error {
o.po.read(rd, o._numArg)
if trace {
outLogger.Printf("connect options: %v", o)
}
return rd.GetError()
}
func (o *connectOptions) write(wr *bufio.Writer) error {
o.po.write(wr)
if trace {
outLogger.Printf("connect options: %v", o)
}
return nil
}

View File

@ -1,38 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=DataType
// DataType is the type definition for data types supported by this package.
type DataType byte
// Data type constants.
const (
DtUnknown DataType = iota // unknown data type
DtTinyint
DtSmallint
DtInteger
DtBigint
DtReal
DtDouble
DtDecimal
DtTime
DtString
DtBytes
DtLob
)

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type=DataType"; DO NOT EDIT.
package protocol
import "strconv"
const _DataType_name = "DtUnknownDtTinyintDtSmallintDtIntegerDtBigintDtRealDtDoubleDtDecimalDtTimeDtStringDtBytesDtLob"
var _DataType_index = [...]uint8{0, 9, 18, 28, 37, 45, 51, 59, 68, 74, 82, 89, 94}
func (i DataType) String() string {
if i >= DataType(len(_DataType_index)-1) {
return "DataType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _DataType_name[_DataType_index[i]:_DataType_index[i+1]]
}

View File

@ -1,20 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package protocol implements the hdb command network protocol.
//
// http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf
package protocol

View File

@ -1,26 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=endianess
type endianess int8
const (
bigEndian endianess = 0
littleEndian endianess = 1
)

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type=endianess"; DO NOT EDIT.
package protocol
import "strconv"
const _endianess_name = "bigEndianlittleEndian"
var _endianess_index = [...]uint8{0, 9, 21}
func (i endianess) String() string {
if i < 0 || i >= endianess(len(_endianess_index)-1) {
return "endianess(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _endianess_name[_endianess_index[i]:_endianess_index[i+1]]
}

View File

@ -1,204 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
sqlStateSize = 5
//bytes of fix length fields mod 8
// - errorCode = 4, errorPosition = 4, errortextLength = 4, errorLevel = 1, sqlState = 5 => 18 bytes
// - 18 mod 8 = 2
fixLength = 2
)
type sqlState [sqlStateSize]byte
type hdbError struct {
errorCode int32
errorPosition int32
errorTextLength int32
errorLevel errorLevel
sqlState sqlState
stmtNo int
errorText []byte
}
// String implements the Stringer interface.
func (e *hdbError) String() string {
return fmt.Sprintf("errorCode %d, errorPosition %d, errorTextLength % d errorLevel %s, sqlState %s stmtNo %d errorText %s",
e.errorCode,
e.errorPosition,
e.errorTextLength,
e.errorLevel,
e.sqlState,
e.stmtNo,
e.errorText,
)
}
// Error implements the Error interface.
func (e *hdbError) Error() string {
if e.stmtNo != -1 {
return fmt.Sprintf("SQL %s %d - %s (statement no: %d)", e.errorLevel, e.errorCode, e.errorText, e.stmtNo)
}
return fmt.Sprintf("SQL %s %d - %s", e.errorLevel, e.errorCode, e.errorText)
}
type hdbErrors struct {
errors []*hdbError
numArg int
idx int
}
// String implements the Stringer interface.
func (e *hdbErrors) String() string {
return e.errors[e.idx].String()
}
// Error implements the golang error interface.
func (e *hdbErrors) Error() string {
return e.errors[e.idx].Error()
}
// NumError implements the driver.Error interface.
func (e *hdbErrors) NumError() int {
return e.numArg
}
// SetIdx implements the driver.Error interface.
func (e *hdbErrors) SetIdx(idx int) {
switch {
case idx < 0:
e.idx = 0
case idx >= e.numArg:
e.idx = e.numArg - 1
default:
e.idx = idx
}
}
// StmtNo implements the driver.Error interface.
func (e *hdbErrors) StmtNo() int {
return e.errors[e.idx].stmtNo
}
// Code implements the driver.Error interface.
func (e *hdbErrors) Code() int {
return int(e.errors[e.idx].errorCode)
}
// Position implements the driver.Error interface.
func (e *hdbErrors) Position() int {
return int(e.errors[e.idx].errorPosition)
}
// Level implements the driver.Error interface.
func (e *hdbErrors) Level() int {
return int(e.errors[e.idx].errorLevel)
}
// Text implements the driver.Error interface.
func (e *hdbErrors) Text() string {
return string(e.errors[e.idx].errorText)
}
// IsWarning implements the driver.Error interface.
func (e *hdbErrors) IsWarning() bool {
return e.errors[e.idx].errorLevel == errorLevelWarning
}
// IsError implements the driver.Error interface.
func (e *hdbErrors) IsError() bool {
return e.errors[e.idx].errorLevel == errorLevelError
}
// IsFatal implements the driver.Error interface.
func (e *hdbErrors) IsFatal() bool {
return e.errors[e.idx].errorLevel == errorLevelFatalError
}
func (e *hdbErrors) setStmtNo(idx, no int) {
if idx >= 0 && idx < e.numArg {
e.errors[idx].stmtNo = no
}
}
func (e *hdbErrors) isWarnings() bool {
for _, _error := range e.errors {
if _error.errorLevel != errorLevelWarning {
return false
}
}
return true
}
func (e *hdbErrors) kind() partKind {
return pkError
}
func (e *hdbErrors) setNumArg(numArg int) {
e.numArg = numArg
}
func (e *hdbErrors) read(rd *bufio.Reader) error {
e.idx = 0 // init error index
if e.errors == nil || e.numArg > cap(e.errors) {
e.errors = make([]*hdbError, e.numArg)
} else {
e.errors = e.errors[:e.numArg]
}
for i := 0; i < e.numArg; i++ {
_error := e.errors[i]
if _error == nil {
_error = new(hdbError)
e.errors[i] = _error
}
_error.stmtNo = -1
_error.errorCode = rd.ReadInt32()
_error.errorPosition = rd.ReadInt32()
_error.errorTextLength = rd.ReadInt32()
_error.errorLevel = errorLevel(rd.ReadInt8())
rd.ReadFull(_error.sqlState[:])
// read error text as ASCII data as some errors return invalid CESU-8 characters
// e.g: SQL HdbError 7 - feature not supported: invalid character encoding: <invaid CESU-8 characters>
// if e.errorText, err = rd.ReadCesu8(int(e.errorTextLength)); err != nil {
// return err
// }
_error.errorText = make([]byte, int(_error.errorTextLength))
rd.ReadFull(_error.errorText)
if trace {
outLogger.Printf("error %d: %s", i, _error)
}
pad := padBytes(int(fixLength + _error.errorTextLength))
if pad != 0 {
rd.Skip(pad)
}
}
return rd.GetError()
}

View File

@ -1,40 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
// ErrorLevel send from database server.
type errorLevel int8
func (e errorLevel) String() string {
switch e {
case 0:
return "Warning"
case 1:
return "Error"
case 2:
return "Fatal Error"
default:
return ""
}
}
// HDB error level constants.
const (
errorLevelWarning errorLevel = 0
errorLevelError errorLevel = 1
errorLevelFatalError errorLevel = 2
)

View File

@ -1,46 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"github.com/SAP/go-hdb/internal/bufio"
)
//fetch size
type fetchsize int32
func (s fetchsize) kind() partKind {
return pkFetchSize
}
func (s fetchsize) size() (int, error) {
return 4, nil
}
func (s fetchsize) numArg() int {
return 1
}
func (s fetchsize) write(wr *bufio.Writer) error {
wr.WriteInt32(int32(s))
if trace {
outLogger.Printf("fetchsize: %d", s)
}
return nil
}

View File

@ -1,774 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"database/sql/driver"
"fmt"
"math"
"sort"
"time"
"github.com/SAP/go-hdb/internal/bufio"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
)
var test uint32
const (
realNullValue uint32 = ^uint32(0)
doubleNullValue uint64 = ^uint64(0)
)
const noFieldName uint32 = 0xFFFFFFFF
type uint32Slice []uint32
func (p uint32Slice) Len() int { return len(p) }
func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p uint32Slice) sort() { sort.Sort(p) }
type fieldNames map[uint32]string
func newFieldNames() fieldNames {
return make(map[uint32]string)
}
func (f fieldNames) addOffset(offset uint32) {
if offset != noFieldName {
f[offset] = ""
}
}
func (f fieldNames) name(offset uint32) string {
if name, ok := f[offset]; ok {
return name
}
return ""
}
func (f fieldNames) setName(offset uint32, name string) {
f[offset] = name
}
func (f fieldNames) sortOffsets() []uint32 {
offsets := make([]uint32, 0, len(f))
for k := range f {
offsets = append(offsets, k)
}
uint32Slice(offsets).sort()
return offsets
}
// FieldValues contains rows read from database.
type FieldValues struct {
rows int
cols int
values []driver.Value
}
func newFieldValues() *FieldValues {
return &FieldValues{}
}
func (f *FieldValues) String() string {
return fmt.Sprintf("rows %d columns %d", f.rows, f.cols)
}
func (f *FieldValues) resize(rows, cols int) {
f.rows, f.cols = rows, cols
f.values = make([]driver.Value, rows*cols)
}
// NumRow returns the number of rows available in FieldValues.
func (f *FieldValues) NumRow() int {
return f.rows
}
// Row fills the dest value slice with row data at index idx.
func (f *FieldValues) Row(idx int, dest []driver.Value) {
copy(dest, f.values[idx*f.cols:(idx+1)*f.cols])
}
const (
tinyintFieldSize = 1
smallintFieldSize = 2
intFieldSize = 4
bigintFieldSize = 8
realFieldSize = 4
doubleFieldSize = 8
dateFieldSize = 4
timeFieldSize = 4
timestampFieldSize = dateFieldSize + timeFieldSize
longdateFieldSize = 8
seconddateFieldSize = 8
daydateFieldSize = 4
secondtimeFieldSize = 4
decimalFieldSize = 16
lobInputDescriptorSize = 9
)
func fieldSize(tc TypeCode, arg driver.NamedValue) (int, error) {
v := arg.Value
if v == nil { //HDB bug: secondtime null value --> see writeField
return 0, nil
}
switch tc {
case tcTinyint:
return tinyintFieldSize, nil
case tcSmallint:
return smallintFieldSize, nil
case tcInteger:
return intFieldSize, nil
case tcBigint:
return bigintFieldSize, nil
case tcReal:
return realFieldSize, nil
case tcDouble:
return doubleFieldSize, nil
case tcDate:
return dateFieldSize, nil
case tcTime:
return timeFieldSize, nil
case tcTimestamp:
return timestampFieldSize, nil
case tcLongdate:
return longdateFieldSize, nil
case tcSeconddate:
return seconddateFieldSize, nil
case tcDaydate:
return daydateFieldSize, nil
case tcSecondtime:
return secondtimeFieldSize, nil
case tcDecimal:
return decimalFieldSize, nil
case tcChar, tcVarchar, tcString:
switch v := v.(type) {
case []byte:
return bytesSize(len(v))
case string:
return bytesSize(len(v))
default:
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
case tcNchar, tcNvarchar, tcNstring:
switch v := v.(type) {
case []byte:
return bytesSize(cesu8.Size(v))
case string:
return bytesSize(cesu8.StringSize(v))
default:
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
case tcBinary, tcVarbinary:
v, ok := v.([]byte)
if !ok {
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
return bytesSize(len(v))
case tcBlob, tcClob, tcNclob:
return lobInputDescriptorSize, nil
}
outLogger.Fatalf("data type %s not implemented", tc)
return 0, nil
}
func readField(session *Session, rd *bufio.Reader, tc TypeCode) (interface{}, error) {
switch tc {
case tcTinyint, tcSmallint, tcInteger, tcBigint:
if !rd.ReadBool() { //null value
return nil, nil
}
switch tc {
case tcTinyint:
return int64(rd.ReadB()), nil
case tcSmallint:
return int64(rd.ReadInt16()), nil
case tcInteger:
return int64(rd.ReadInt32()), nil
case tcBigint:
return rd.ReadInt64(), nil
}
case tcReal:
v := rd.ReadUint32()
if v == realNullValue {
return nil, nil
}
return float64(math.Float32frombits(v)), nil
case tcDouble:
v := rd.ReadUint64()
if v == doubleNullValue {
return nil, nil
}
return math.Float64frombits(v), nil
case tcDate:
year, month, day, null := readDate(rd)
if null {
return nil, nil
}
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC), nil
// time read gives only seconds (cut), no milliseconds
case tcTime:
hour, minute, nanosecs, null := readTime(rd)
if null {
return nil, nil
}
return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil
case tcTimestamp:
year, month, day, dateNull := readDate(rd)
hour, minute, nanosecs, timeNull := readTime(rd)
if dateNull || timeNull {
return nil, nil
}
return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), nil
case tcLongdate:
time, null := readLongdate(rd)
if null {
return nil, nil
}
return time, nil
case tcSeconddate:
time, null := readSeconddate(rd)
if null {
return nil, nil
}
return time, nil
case tcDaydate:
time, null := readDaydate(rd)
if null {
return nil, nil
}
return time, nil
case tcSecondtime:
time, null := readSecondtime(rd)
if null {
return nil, nil
}
return time, nil
case tcDecimal:
b, null := readDecimal(rd)
if null {
return nil, nil
}
return b, nil
case tcChar, tcVarchar:
value, null := readBytes(rd)
if null {
return nil, nil
}
return value, nil
case tcNchar, tcNvarchar:
value, null := readUtf8(rd)
if null {
return nil, nil
}
return value, nil
case tcBinary, tcVarbinary:
value, null := readBytes(rd)
if null {
return nil, nil
}
return value, nil
case tcBlob, tcClob, tcNclob:
null, writer, err := readLob(session, rd, tc)
if null {
return nil, nil
}
return writer, err
}
outLogger.Fatalf("read field: type code %s not implemented", tc)
return nil, nil
}
func writeField(wr *bufio.Writer, tc TypeCode, arg driver.NamedValue) error {
v := arg.Value
//HDB bug: secondtime null value cannot be set by setting high byte
// trying so, gives
// SQL HdbError 1033 - error while parsing protocol: no such data type: type_code=192, index=2
// null value
//if v == nil && tc != tcSecondtime
if v == nil {
wr.WriteB(byte(tc) | 0x80) //set high bit
return nil
}
// type code
wr.WriteB(byte(tc))
switch tc {
default:
outLogger.Fatalf("write field: type code %s not implemented", tc)
case tcTinyint, tcSmallint, tcInteger, tcBigint:
var i64 int64
switch v := v.(type) {
default:
return fmt.Errorf("invalid argument type %T", v)
case bool:
if v {
i64 = 1
} else {
i64 = 0
}
case int64:
i64 = v
}
switch tc {
case tcTinyint:
wr.WriteB(byte(i64))
case tcSmallint:
wr.WriteInt16(int16(i64))
case tcInteger:
wr.WriteInt32(int32(i64))
case tcBigint:
wr.WriteInt64(i64)
}
case tcReal:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
wr.WriteFloat32(float32(f64))
case tcDouble:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
wr.WriteFloat64(f64)
case tcDate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDate(wr, t)
case tcTime:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeTime(wr, t)
case tcTimestamp:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDate(wr, t)
writeTime(wr, t)
case tcLongdate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeLongdate(wr, t)
case tcSeconddate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeSeconddate(wr, t)
case tcDaydate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDaydate(wr, t)
case tcSecondtime:
// HDB bug: write null value explicite
if v == nil {
wr.WriteInt32(86401)
return nil
}
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeSecondtime(wr, t)
case tcDecimal:
b, ok := v.([]byte)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
if len(b) != 16 {
return fmt.Errorf("invalid argument length %d of type %T - expected %d", len(b), v, 16)
}
wr.Write(b)
case tcChar, tcVarchar, tcString:
switch v := v.(type) {
case []byte:
writeBytes(wr, v)
case string:
writeString(wr, v)
default:
return fmt.Errorf("invalid argument type %T", v)
}
case tcNchar, tcNvarchar, tcNstring:
switch v := v.(type) {
case []byte:
writeUtf8Bytes(wr, v)
case string:
writeUtf8String(wr, v)
default:
return fmt.Errorf("invalid argument type %T", v)
}
case tcBinary, tcVarbinary:
v, ok := v.([]byte)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeBytes(wr, v)
case tcBlob, tcClob, tcNclob:
writeLob(wr)
}
return nil
}
// null values: most sig bit unset
// year: unset second most sig bit (subtract 2^15)
// --> read year as unsigned
// month is 0-based
// day is 1 byte
func readDate(rd *bufio.Reader) (int, time.Month, int, bool) {
year := rd.ReadUint16()
null := ((year & 0x8000) == 0) //null value
year &= 0x3fff
month := rd.ReadInt8()
month++
day := rd.ReadInt8()
return int(year), time.Month(month), int(day), null
}
// year: set most sig bit
// month 0 based
func writeDate(wr *bufio.Writer, t time.Time) {
//store in utc
utc := t.In(time.UTC)
year, month, day := utc.Date()
wr.WriteUint16(uint16(year) | 0x8000)
wr.WriteInt8(int8(month) - 1)
wr.WriteInt8(int8(day))
}
func readTime(rd *bufio.Reader) (int, int, int, bool) {
hour := rd.ReadB()
null := (hour & 0x80) == 0 //null value
hour &= 0x7f
minute := rd.ReadInt8()
millisecs := rd.ReadUint16()
nanosecs := int(millisecs) * 1000000
return int(hour), int(minute), nanosecs, null
}
func writeTime(wr *bufio.Writer, t time.Time) {
//store in utc
utc := t.UTC()
wr.WriteB(byte(utc.Hour()) | 0x80)
wr.WriteInt8(int8(utc.Minute()))
millisecs := utc.Second()*1000 + utc.Round(time.Millisecond).Nanosecond()/1000000
wr.WriteUint16(uint16(millisecs))
}
var zeroTime = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
func readLongdate(rd *bufio.Reader) (time.Time, bool) {
longdate := rd.ReadInt64()
if longdate == 3155380704000000001 { // null value
return zeroTime, true
}
return convertLongdateToTime(longdate), false
}
func writeLongdate(wr *bufio.Writer, t time.Time) {
wr.WriteInt64(convertTimeToLongdate(t))
}
func readSeconddate(rd *bufio.Reader) (time.Time, bool) {
seconddate := rd.ReadInt64()
if seconddate == 315538070401 { // null value
return zeroTime, true
}
return convertSeconddateToTime(seconddate), false
}
func writeSeconddate(wr *bufio.Writer, t time.Time) {
wr.WriteInt64(convertTimeToSeconddate(t))
}
func readDaydate(rd *bufio.Reader) (time.Time, bool) {
daydate := rd.ReadInt32()
if daydate == 3652062 { // null value
return zeroTime, true
}
return convertDaydateToTime(int64(daydate)), false
}
func writeDaydate(wr *bufio.Writer, t time.Time) {
wr.WriteInt32(int32(convertTimeToDayDate(t)))
}
func readSecondtime(rd *bufio.Reader) (time.Time, bool) {
secondtime := rd.ReadInt32()
if secondtime == 86401 { // null value
return zeroTime, true
}
return convertSecondtimeToTime(int(secondtime)), false
}
func writeSecondtime(wr *bufio.Writer, t time.Time) {
wr.WriteInt32(int32(convertTimeToSecondtime(t)))
}
// nanosecond: HDB - 7 digits precision (not 9 digits)
func convertTimeToLongdate(t time.Time) int64 {
t = t.UTC()
return (((((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60)+int64(t.Second()))*10000000 + int64(t.Nanosecond()/100) + 1
}
func convertLongdateToTime(longdate int64) time.Time {
const dayfactor = 10000000 * 24 * 60 * 60
longdate--
d := (longdate % dayfactor) * 100
t := convertDaydateToTime((longdate / dayfactor) + 1)
return t.Add(time.Duration(d))
}
func convertTimeToSeconddate(t time.Time) int64 {
t = t.UTC()
return (((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60 + int64(t.Second()) + 1
}
func convertSeconddateToTime(seconddate int64) time.Time {
const dayfactor = 24 * 60 * 60
seconddate--
d := (seconddate % dayfactor) * 1000000000
t := convertDaydateToTime((seconddate / dayfactor) + 1)
return t.Add(time.Duration(d))
}
const julianHdb = 1721423 // 1 January 0001 00:00:00 (1721424) - 1
func convertTimeToDayDate(t time.Time) int64 {
return int64(timeToJulianDay(t) - julianHdb)
}
func convertDaydateToTime(daydate int64) time.Time {
return julianDayToTime(int(daydate) + julianHdb)
}
func convertTimeToSecondtime(t time.Time) int {
t = t.UTC()
return (t.Hour()*60+t.Minute())*60 + t.Second() + 1
}
func convertSecondtimeToTime(secondtime int) time.Time {
return time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(int64(secondtime-1) * 1000000000))
}
func readDecimal(rd *bufio.Reader) ([]byte, bool) {
b := make([]byte, 16)
rd.ReadFull(b)
if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set)
return nil, true
}
return b, false
}
// string / binary length indicators
const (
bytesLenIndNullValue byte = 255
bytesLenIndSmall byte = 245
bytesLenIndMedium byte = 246
bytesLenIndBig byte = 247
)
func bytesSize(size int) (int, error) { //size + length indicator
switch {
default:
return 0, fmt.Errorf("max string length %d exceeded %d", math.MaxInt32, size)
case size <= int(bytesLenIndSmall):
return size + 1, nil
case size <= math.MaxInt16:
return size + 3, nil
case size <= math.MaxInt32:
return size + 5, nil
}
}
func readBytesSize(rd *bufio.Reader) (int, bool) {
ind := rd.ReadB() //length indicator
switch {
default:
return 0, false
case ind == bytesLenIndNullValue:
return 0, true
case ind <= bytesLenIndSmall:
return int(ind), false
case ind == bytesLenIndMedium:
return int(rd.ReadInt16()), false
case ind == bytesLenIndBig:
return int(rd.ReadInt32()), false
}
}
func writeBytesSize(wr *bufio.Writer, size int) error {
switch {
default:
return fmt.Errorf("max argument length %d of string exceeded", size)
case size <= int(bytesLenIndSmall):
wr.WriteB(byte(size))
case size <= math.MaxInt16:
wr.WriteB(bytesLenIndMedium)
wr.WriteInt16(int16(size))
case size <= math.MaxInt32:
wr.WriteB(bytesLenIndBig)
wr.WriteInt32(int32(size))
}
return nil
}
func readBytes(rd *bufio.Reader) ([]byte, bool) {
size, null := readBytesSize(rd)
if null {
return nil, true
}
b := make([]byte, size)
rd.ReadFull(b)
return b, false
}
func readUtf8(rd *bufio.Reader) ([]byte, bool) {
size, null := readBytesSize(rd)
if null {
return nil, true
}
b := rd.ReadCesu8(size)
return b, false
}
// strings with one byte length
func readShortUtf8(rd *bufio.Reader) ([]byte, int) {
size := rd.ReadB()
b := rd.ReadCesu8(int(size))
return b, int(size)
}
func writeBytes(wr *bufio.Writer, b []byte) {
writeBytesSize(wr, len(b))
wr.Write(b)
}
func writeString(wr *bufio.Writer, s string) {
writeBytesSize(wr, len(s))
wr.WriteString(s)
}
func writeUtf8Bytes(wr *bufio.Writer, b []byte) {
size := cesu8.Size(b)
writeBytesSize(wr, size)
wr.WriteCesu8(b)
}
func writeUtf8String(wr *bufio.Writer, s string) {
size := cesu8.StringSize(s)
writeBytesSize(wr, size)
wr.WriteStringCesu8(s)
}
func readLob(s *Session, rd *bufio.Reader, tc TypeCode) (bool, lobChunkWriter, error) {
rd.ReadInt8() // type code (is int here)
opt := rd.ReadInt8()
null := (lobOptions(opt) & loNullindicator) != 0
if null {
return true, nil, nil
}
eof := (lobOptions(opt) & loLastdata) != 0
rd.Skip(2)
charLen := rd.ReadInt64()
byteLen := rd.ReadInt64()
id := rd.ReadUint64()
chunkLen := rd.ReadInt32()
lobChunkWriter := newLobChunkWriter(tc.isCharBased(), s, locatorID(id), charLen, byteLen)
if err := lobChunkWriter.write(rd, int(chunkLen), eof); err != nil {
return null, lobChunkWriter, err
}
return null, lobChunkWriter, nil
}
// TODO: first write: add content? - actually no data transferred
func writeLob(wr *bufio.Writer) {
wr.WriteB(0)
wr.WriteInt32(0)
wr.WriteInt32(0)
}

View File

@ -1,60 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=functionCode
type functionCode int16
const (
fcNil functionCode = 0
fcDDL functionCode = 1
fcInsert functionCode = 2
fcUpdate functionCode = 3
fcDelete functionCode = 4
fcSelect functionCode = 5
fcSelectForUpdate functionCode = 6
fcExplain functionCode = 7
fcDBProcedureCall functionCode = 8
fcDBProcedureCallWithResult functionCode = 9
fcFetch functionCode = 10
fcCommit functionCode = 11
fcRollback functionCode = 12
fcSavepoint functionCode = 13
fcConnect functionCode = 14
fcWriteLob functionCode = 15
fcReadLob functionCode = 16
fcPing functionCode = 17 //reserved: do not use
fcDisconnect functionCode = 18
fcCloseCursor functionCode = 19
fcFindLob functionCode = 20
fcAbapStream functionCode = 21
fcXAStart functionCode = 22
fcXAJoin functionCode = 23
)
func (k functionCode) queryType() QueryType {
switch k {
default:
return QtNone
case fcSelect, fcSelectForUpdate:
return QtSelect
case fcDBProcedureCall:
return QtProcedureCall
}
}

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type=functionCode"; DO NOT EDIT.
package protocol
import "strconv"
const _functionCode_name = "fcNilfcDDLfcInsertfcUpdatefcDeletefcSelectfcSelectForUpdatefcExplainfcDBProcedureCallfcDBProcedureCallWithResultfcFetchfcCommitfcRollbackfcSavepointfcConnectfcWriteLobfcReadLobfcPingfcDisconnectfcCloseCursorfcFindLobfcAbapStreamfcXAStartfcXAJoin"
var _functionCode_index = [...]uint8{0, 5, 10, 18, 26, 34, 42, 59, 68, 85, 112, 119, 127, 137, 148, 157, 167, 176, 182, 194, 207, 216, 228, 237, 245}
func (i functionCode) String() string {
if i < 0 || i >= functionCode(len(_functionCode_index)-1) {
return "functionCode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _functionCode_name[_functionCode_index[i]:_functionCode_index[i+1]]
}

View File

@ -1,198 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
okEndianess int8 = 1
)
const (
initRequestFillerSize = 4
)
var initRequestFiller uint32 = 0xffffffff
type productVersion struct {
major int8
minor int16
}
func (v *productVersion) String() string {
return fmt.Sprintf("%d.%d", v.major, v.minor)
}
type protocolVersion struct {
major int8
minor int16
}
func (v *protocolVersion) String() string {
return fmt.Sprintf("%d.%d", v.major, v.minor)
}
type version struct {
major int8
minor int16
}
func (v *version) String() string {
return fmt.Sprintf("%d.%d", v.major, v.minor)
}
type initRequest struct {
product *version
protocol *version
numOptions int8
endianess endianess
}
func newInitRequest() *initRequest {
return &initRequest{
product: new(version),
protocol: new(version),
}
}
func (r *initRequest) String() string {
switch r.numOptions {
default:
return fmt.Sprintf("init request: product version %s protocol version %s", r.product, r.protocol)
case 1:
return fmt.Sprintf("init request: product version %s protocol version %s endianess %s", r.product, r.protocol, r.endianess)
}
}
func (r *initRequest) read(rd *bufio.Reader) error {
rd.Skip(initRequestFillerSize) //filler
r.product.major = rd.ReadInt8()
r.product.minor = rd.ReadInt16()
r.protocol.major = rd.ReadInt8()
r.protocol.minor = rd.ReadInt16()
rd.Skip(1) //reserved filler
r.numOptions = rd.ReadInt8()
switch r.numOptions {
default:
outLogger.Fatalf("invalid number of options %d", r.numOptions)
case 0:
rd.Skip(2)
case 1:
cnt := rd.ReadInt8()
if cnt != 1 {
outLogger.Fatalf("endianess %d - 1 expected", cnt)
}
r.endianess = endianess(rd.ReadInt8())
}
if trace {
outLogger.Printf("read %s", r)
}
return rd.GetError()
}
func (r *initRequest) write(wr *bufio.Writer) error {
wr.WriteUint32(initRequestFiller)
wr.WriteInt8(r.product.major)
wr.WriteInt16(r.product.minor)
wr.WriteInt8(r.protocol.major)
wr.WriteInt16(r.protocol.minor)
switch r.numOptions {
default:
outLogger.Fatalf("invalid number of options %d", r.numOptions)
case 0:
wr.WriteZeroes(4)
case 1:
// reserved
wr.WriteZeroes(1)
wr.WriteInt8(r.numOptions)
wr.WriteInt8(int8(okEndianess))
wr.WriteInt8(int8(r.endianess))
}
// flush
if err := wr.Flush(); err != nil {
return err
}
if trace {
outLogger.Printf("write %s", r)
}
return nil
}
type initReply struct {
product *version
protocol *version
}
func newInitReply() *initReply {
return &initReply{
product: new(version),
protocol: new(version),
}
}
func (r *initReply) String() string {
return fmt.Sprintf("init reply: product version %s protocol version %s", r.product, r.protocol)
}
func (r *initReply) read(rd *bufio.Reader) error {
r.product.major = rd.ReadInt8()
r.product.minor = rd.ReadInt16()
r.protocol.major = rd.ReadInt8()
r.protocol.minor = rd.ReadInt16()
rd.Skip(2) //commitInitReplySize
if trace {
outLogger.Printf("read %s", r)
}
return rd.GetError()
}
func (r *initReply) write(wr *bufio.Writer) error {
wr.WriteInt8(r.product.major)
wr.WriteInt16(r.product.minor)
wr.WriteInt8(r.product.major)
wr.WriteInt16(r.protocol.minor)
wr.WriteZeroes(2) // commitInitReplySize
// flush
if err := wr.Flush(); err != nil {
return err
}
if trace {
outLogger.Printf("write %s", r)
}
return nil
}

View File

@ -1,23 +0,0 @@
// +build amd64 386 arm arm64 ppc64le mipsle mips64le
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//amd64, 386 architectures: little endian
//arm, arm64: go supports little endian only
var archEndian = littleEndian

View File

@ -1,507 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"io"
"math"
"unicode/utf8"
"golang.org/x/text/transform"
"github.com/SAP/go-hdb/internal/bufio"
"github.com/SAP/go-hdb/internal/unicode"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
)
const (
locatorIDSize = 8
writeLobRequestHeaderSize = 21
readLobRequestSize = 24
)
// variable (unit testing)
//var lobChunkSize = 1 << 14 //TODO: check size
var lobChunkSize int32 = 4096 //TODO: check size
//lob options
type lobOptions int8
const (
loNullindicator lobOptions = 0x01
loDataincluded lobOptions = 0x02
loLastdata lobOptions = 0x04
)
var lobOptionsText = map[lobOptions]string{
loNullindicator: "null indicator",
loDataincluded: "data included",
loLastdata: "last data",
}
func (k lobOptions) String() string {
t := make([]string, 0, len(lobOptionsText))
for option, text := range lobOptionsText {
if (k & option) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
type locatorID uint64 // byte[locatorIdSize]
// write lob reply
type writeLobReply struct {
ids []locatorID
numArg int
}
func (r *writeLobReply) String() string {
return fmt.Sprintf("write lob reply: %v", r.ids)
}
func (r *writeLobReply) kind() partKind {
return pkWriteLobReply
}
func (r *writeLobReply) setNumArg(numArg int) {
r.numArg = numArg
}
func (r *writeLobReply) read(rd *bufio.Reader) error {
//resize ids
if r.ids == nil || cap(r.ids) < r.numArg {
r.ids = make([]locatorID, r.numArg)
} else {
r.ids = r.ids[:r.numArg]
}
for i := 0; i < r.numArg; i++ {
r.ids[i] = locatorID(rd.ReadUint64())
}
return rd.GetError()
}
//write lob request
type writeLobRequest struct {
lobPrmFields []*ParameterField
}
func (r *writeLobRequest) kind() partKind {
return pkWriteLobRequest
}
func (r *writeLobRequest) size() (int, error) {
// TODO: check size limit
size := 0
for _, prmField := range r.lobPrmFields {
cr := prmField.chunkReader
if cr.done() {
continue
}
if err := cr.fill(); err != nil {
return 0, err
}
size += writeLobRequestHeaderSize
size += cr.size()
}
return size, nil
}
func (r *writeLobRequest) numArg() int {
n := 0
for _, prmField := range r.lobPrmFields {
cr := prmField.chunkReader
if !cr.done() {
n++
}
}
return n
}
func (r *writeLobRequest) write(wr *bufio.Writer) error {
for _, prmField := range r.lobPrmFields {
cr := prmField.chunkReader
if !cr.done() {
wr.WriteUint64(uint64(prmField.lobLocatorID))
opt := int8(0x02) // data included
if cr.eof() {
opt |= 0x04 // last data
}
wr.WriteInt8(opt)
wr.WriteInt64(-1) //offset (-1 := append)
wr.WriteInt32(int32(cr.size())) // size
wr.Write(cr.bytes())
}
}
return nil
}
//read lob request
type readLobRequest struct {
w lobChunkWriter
}
func (r *readLobRequest) kind() partKind {
return pkReadLobRequest
}
func (r *readLobRequest) size() (int, error) {
return readLobRequestSize, nil
}
func (r *readLobRequest) numArg() int {
return 1
}
func (r *readLobRequest) write(wr *bufio.Writer) error {
wr.WriteUint64(uint64(r.w.id()))
readOfs, readLen := r.w.readOfsLen()
wr.WriteInt64(readOfs + 1) //1-based
wr.WriteInt32(readLen)
wr.WriteZeroes(4)
return nil
}
// read lob reply
// - seems like readLobreply gives only an result for one lob - even if more then one is requested
// --> read single lobs
type readLobReply struct {
w lobChunkWriter
}
func (r *readLobReply) kind() partKind {
return pkReadLobReply
}
func (r *readLobReply) setNumArg(numArg int) {
if numArg != 1 {
panic("numArg == 1 expected")
}
}
func (r *readLobReply) read(rd *bufio.Reader) error {
id := rd.ReadUint64()
if r.w.id() != locatorID(id) {
return fmt.Errorf("internal error: invalid lob locator %d - expected %d", id, r.w.id())
}
opt := rd.ReadInt8()
chunkLen := rd.ReadInt32()
rd.Skip(3)
eof := (lobOptions(opt) & loLastdata) != 0
if err := r.w.write(rd, int(chunkLen), eof); err != nil {
return err
}
return rd.GetError()
}
// lobChunkReader reads lob field io.Reader in chunks for writing to db.
type lobChunkReader interface {
fill() error
size() int
bytes() []byte
eof() bool
done() bool
}
func newLobChunkReader(isCharBased bool, r io.Reader) lobChunkReader {
if isCharBased {
return &charLobChunkReader{r: r}
}
return &binaryLobChunkReader{r: r}
}
// binaryLobChunkReader (byte based chunks).
type binaryLobChunkReader struct {
r io.Reader
_size int
_eof bool
_done bool
b []byte
}
func (l *binaryLobChunkReader) eof() bool { return l._eof }
func (l *binaryLobChunkReader) done() bool { return l._done }
func (l *binaryLobChunkReader) size() int { return l._size }
func (l *binaryLobChunkReader) bytes() []byte {
l._done = l._eof
return l.b[:l._size]
}
func (l *binaryLobChunkReader) fill() error {
if l._eof {
return io.EOF
}
var err error
l.b = resizeBuffer(l.b, int(lobChunkSize))
l._size, err = l.r.Read(l.b)
if err != nil && err != io.EOF {
return err
}
l._eof = err == io.EOF
return nil
}
// charLobChunkReader (cesu8 character based chunks).
type charLobChunkReader struct {
r io.Reader
_size int
_eof bool
_done bool
b []byte
c []byte
ofs int
}
func (l *charLobChunkReader) eof() bool { return l._eof }
func (l *charLobChunkReader) done() bool { return l._done }
func (l *charLobChunkReader) size() int { return l._size }
func (l *charLobChunkReader) bytes() []byte {
l._done = l._eof
return l.b[:l._size]
}
func (l *charLobChunkReader) fill() error {
if l._eof {
return io.EOF
}
l.c = resizeBuffer(l.c, int(lobChunkSize)+l.ofs)
n, err := l.r.Read(l.c[l.ofs:])
size := n + l.ofs
if err != nil && err != io.EOF {
return err
}
l._eof = err == io.EOF
if l._eof && size == 0 {
l._size = 0
return nil
}
l.b = resizeBuffer(l.b, cesu8.Size(l.c[:size])) // last rune might be incomplete, so size is one greater than needed
nDst, nSrc, err := unicode.Utf8ToCesu8Transformer.Transform(l.b, l.c[:size], l._eof)
if err != nil && err != transform.ErrShortSrc {
return err
}
if l._eof && err == transform.ErrShortSrc {
return unicode.ErrInvalidUtf8
}
l._size = nDst
l.ofs = size - nSrc
if l.ofs > 0 {
copy(l.c, l.c[nSrc:size]) // copy rest to buffer beginn
}
return nil
}
// lobChunkWriter reads db lob chunks and writes them into lob field io.Writer.
type lobChunkWriter interface {
SetWriter(w io.Writer) error // gets called by driver.Lob.Scan
id() locatorID
write(rd *bufio.Reader, size int, eof bool) error
readOfsLen() (int64, int32)
eof() bool
}
func newLobChunkWriter(isCharBased bool, s *Session, id locatorID, charLen, byteLen int64) lobChunkWriter {
if isCharBased {
return &charLobChunkWriter{s: s, _id: id, charLen: charLen, byteLen: byteLen}
}
return &binaryLobChunkWriter{s: s, _id: id, charLen: charLen, byteLen: byteLen}
}
// binaryLobChunkWriter (byte based lobs).
type binaryLobChunkWriter struct {
s *Session
_id locatorID
charLen int64
byteLen int64
readOfs int64
_eof bool
ofs int
wr io.Writer
b []byte
}
func (l *binaryLobChunkWriter) id() locatorID { return l._id }
func (l *binaryLobChunkWriter) eof() bool { return l._eof }
func (l *binaryLobChunkWriter) SetWriter(wr io.Writer) error {
l.wr = wr
if err := l.flush(); err != nil {
return err
}
return l.s.readLobStream(l)
}
func (l *binaryLobChunkWriter) write(rd *bufio.Reader, size int, eof bool) error {
l._eof = eof // store eof
if size == 0 {
return nil
}
l.b = resizeBuffer(l.b, size+l.ofs)
rd.ReadFull(l.b[l.ofs:])
if l.wr != nil {
return l.flush()
}
return nil
}
func (l *binaryLobChunkWriter) readOfsLen() (int64, int32) {
readLen := l.charLen - l.readOfs
if readLen > int64(math.MaxInt32) || readLen > int64(lobChunkSize) {
return l.readOfs, lobChunkSize
}
return l.readOfs, int32(readLen)
}
func (l *binaryLobChunkWriter) flush() error {
if _, err := l.wr.Write(l.b); err != nil {
return err
}
l.readOfs += int64(len(l.b))
return nil
}
type charLobChunkWriter struct {
s *Session
_id locatorID
charLen int64
byteLen int64
readOfs int64
_eof bool
ofs int
wr io.Writer
b []byte
}
func (l *charLobChunkWriter) id() locatorID { return l._id }
func (l *charLobChunkWriter) eof() bool { return l._eof }
func (l *charLobChunkWriter) SetWriter(wr io.Writer) error {
l.wr = wr
if err := l.flush(); err != nil {
return err
}
return l.s.readLobStream(l)
}
func (l *charLobChunkWriter) write(rd *bufio.Reader, size int, eof bool) error {
l._eof = eof // store eof
if size == 0 {
return nil
}
l.b = resizeBuffer(l.b, size+l.ofs)
rd.ReadFull(l.b[l.ofs:])
if l.wr != nil {
return l.flush()
}
return nil
}
func (l *charLobChunkWriter) readOfsLen() (int64, int32) {
readLen := l.charLen - l.readOfs
if readLen > int64(math.MaxInt32) || readLen > int64(lobChunkSize) {
return l.readOfs, lobChunkSize
}
return l.readOfs, int32(readLen)
}
func (l *charLobChunkWriter) flush() error {
nDst, nSrc, err := unicode.Cesu8ToUtf8Transformer.Transform(l.b, l.b, true) // inline cesu8 to utf8 transformation
if err != nil && err != transform.ErrShortSrc {
return err
}
if _, err := l.wr.Write(l.b[:nDst]); err != nil {
return err
}
l.ofs = len(l.b) - nSrc
if l.ofs != 0 && l.ofs != cesu8.CESUMax/2 { // assert remaining bytes
return unicode.ErrInvalidCesu8
}
l.readOfs += int64(l.runeCount(l.b[:nDst]))
if l.ofs != 0 {
l.readOfs++ // add half encoding
copy(l.b, l.b[nSrc:len(l.b)]) // move half encoding to buffer begin
}
return nil
}
// Caution: hdb counts 4 byte utf-8 encodings (cesu-8 6 bytes) as 2 (3 byte) chars
func (l *charLobChunkWriter) runeCount(b []byte) int {
numChars := 0
for len(b) > 0 {
_, size := utf8.DecodeRune(b)
b = b[size:]
numChars++
if size == utf8.UTFMax {
numChars++
}
}
return numChars
}
// helper
func resizeBuffer(b1 []byte, size int) []byte {
if b1 == nil || cap(b1) < size {
b2 := make([]byte, size)
copy(b2, b1) // !!!
return b2
}
return b1[:size]
}

View File

@ -1,75 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
messageHeaderSize = 32
)
//message header
type messageHeader struct {
sessionID int64
packetCount int32
varPartLength uint32
varPartSize uint32
noOfSegm int16
}
func (h *messageHeader) String() string {
return fmt.Sprintf("session id %d packetCount %d varPartLength %d, varPartSize %d noOfSegm %d",
h.sessionID,
h.packetCount,
h.varPartLength,
h.varPartSize,
h.noOfSegm)
}
func (h *messageHeader) write(wr *bufio.Writer) error {
wr.WriteInt64(h.sessionID)
wr.WriteInt32(h.packetCount)
wr.WriteUint32(h.varPartLength)
wr.WriteUint32(h.varPartSize)
wr.WriteInt16(h.noOfSegm)
wr.WriteZeroes(10) //messageHeaderSize
if trace {
outLogger.Printf("write message header: %s", h)
}
return nil
}
func (h *messageHeader) read(rd *bufio.Reader) error {
h.sessionID = rd.ReadInt64()
h.packetCount = rd.ReadInt32()
h.varPartLength = rd.ReadUint32()
h.varPartSize = rd.ReadUint32()
h.noOfSegm = rd.ReadInt16()
rd.Skip(10) //messageHeaderSize
if trace {
outLogger.Printf("read message header: %s", h)
}
return rd.GetError()
}

View File

@ -1,49 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=messageType
type messageType int8
const (
mtNil messageType = 0
mtExecuteDirect messageType = 2
mtPrepare messageType = 3
mtAbapStream messageType = 4
mtXAStart messageType = 5
mtXAJoin messageType = 6
mtExecute messageType = 13
mtWriteLob messageType = 16
mtReadLob messageType = 17
mtFindLob messageType = 18
mtAuthenticate messageType = 65
mtConnect messageType = 66
mtCommit messageType = 67
mtRollback messageType = 68
mtCloseResultset messageType = 69
mtDropStatementID messageType = 70
mtFetchNext messageType = 71
mtFetchAbsolute messageType = 72
mtFetchRelative messageType = 73
mtFetchFirst messageType = 74
mtFetchLast messageType = 75
mtDisconnect messageType = 77
mtExecuteITab messageType = 78
mtFetchNextITab messageType = 79
mtInsertNextITab messageType = 80
)

View File

@ -1,44 +0,0 @@
// Code generated by "stringer -type=messageType"; DO NOT EDIT.
package protocol
import "strconv"
const (
_messageType_name_0 = "mtNil"
_messageType_name_1 = "mtExecuteDirectmtPreparemtAbapStreammtXAStartmtXAJoin"
_messageType_name_2 = "mtExecute"
_messageType_name_3 = "mtWriteLobmtReadLobmtFindLob"
_messageType_name_4 = "mtAuthenticatemtConnectmtCommitmtRollbackmtCloseResultsetmtDropStatementIDmtFetchNextmtFetchAbsolutemtFetchRelativemtFetchFirstmtFetchLast"
_messageType_name_5 = "mtDisconnectmtExecuteITabmtFetchNextITabmtInsertNextITab"
)
var (
_messageType_index_1 = [...]uint8{0, 15, 24, 36, 45, 53}
_messageType_index_3 = [...]uint8{0, 10, 19, 28}
_messageType_index_4 = [...]uint8{0, 14, 23, 31, 41, 57, 74, 85, 100, 115, 127, 138}
_messageType_index_5 = [...]uint8{0, 12, 25, 40, 56}
)
func (i messageType) String() string {
switch {
case i == 0:
return _messageType_name_0
case 2 <= i && i <= 6:
i -= 2
return _messageType_name_1[_messageType_index_1[i]:_messageType_index_1[i+1]]
case i == 13:
return _messageType_name_2
case 16 <= i && i <= 18:
i -= 16
return _messageType_name_3[_messageType_index_3[i]:_messageType_index_3[i+1]]
case 65 <= i && i <= 75:
i -= 65
return _messageType_name_4[_messageType_index_4[i]:_messageType_index_4[i+1]]
case 77 <= i && i <= 80:
i -= 77
return _messageType_name_5[_messageType_index_5[i]:_messageType_index_5[i+1]]
default:
return "messageType(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@ -1,188 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
type booleanType bool
func (t booleanType) String() string {
return fmt.Sprintf("%t", t)
}
type intType int32
func (t intType) String() string {
return fmt.Sprintf("%d", t)
}
type bigintType int64
func (t bigintType) String() string {
return fmt.Sprintf("%d", t)
}
type doubleType float64
func (t doubleType) String() string {
return fmt.Sprintf("%g", t)
}
type stringType []byte
type binaryStringType []byte
func (t binaryStringType) String() string {
return fmt.Sprintf("%v", []byte(t))
}
//multi line options (number of lines in part header argumentCount)
type multiLineOptions []plainOptions
func (o multiLineOptions) size() int {
size := 0
for _, m := range o {
size += m.size()
}
return size
}
//pointer: append multiLineOptions itself
func (o *multiLineOptions) read(rd *bufio.Reader, lineCnt int) {
for i := 0; i < lineCnt; i++ {
m := plainOptions{}
cnt := rd.ReadInt16()
m.read(rd, int(cnt))
*o = append(*o, m)
}
}
func (o multiLineOptions) write(wr *bufio.Writer) {
for _, m := range o {
wr.WriteInt16(int16(len(m)))
m.write(wr)
}
}
type plainOptions map[int8]interface{}
func (o plainOptions) size() int {
size := 2 * len(o) //option + type
for _, v := range o {
switch v := v.(type) {
default:
outLogger.Fatalf("type %T not implemented", v)
case booleanType:
size++
case intType:
size += 4
case bigintType:
size += 8
case doubleType:
size += 8
case stringType:
size += (2 + len(v)) //length int16 + string length
case binaryStringType:
size += (2 + len(v)) //length int16 + string length
}
}
return size
}
func (o plainOptions) read(rd *bufio.Reader, cnt int) {
for i := 0; i < cnt; i++ {
k := rd.ReadInt8()
tc := rd.ReadB()
switch TypeCode(tc) {
default:
outLogger.Fatalf("type code %s not implemented", TypeCode(tc))
case tcBoolean:
o[k] = booleanType(rd.ReadBool())
case tcInteger:
o[k] = intType(rd.ReadInt32())
case tcBigint:
o[k] = bigintType(rd.ReadInt64())
case tcDouble:
o[k] = doubleType(rd.ReadFloat64())
case tcString:
size := rd.ReadInt16()
v := make([]byte, size)
rd.ReadFull(v)
o[k] = stringType(v)
case tcBstring:
size := rd.ReadInt16()
v := make([]byte, size)
rd.ReadFull(v)
o[k] = binaryStringType(v)
}
}
}
func (o plainOptions) write(wr *bufio.Writer) {
for k, v := range o {
wr.WriteInt8(k)
switch v := v.(type) {
default:
outLogger.Fatalf("type %T not implemented", v)
case booleanType:
wr.WriteInt8(int8(tcBoolean))
wr.WriteBool(bool(v))
case intType:
wr.WriteInt8(int8(tcInteger))
wr.WriteInt32(int32(v))
case bigintType:
wr.WriteInt8(int8(tcBigint))
wr.WriteInt64(int64(v))
case doubleType:
wr.WriteInt8(int8(tcDouble))
wr.WriteFloat64(float64(v))
case stringType:
wr.WriteInt8(int8(tcString))
wr.WriteInt16(int16(len(v)))
wr.Write(v)
case binaryStringType:
wr.WriteInt8(int8(tcBstring))
wr.WriteInt16(int16(len(v)))
wr.Write(v)
}
}
}

View File

@ -1,389 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"database/sql/driver"
"fmt"
"io"
"github.com/SAP/go-hdb/internal/bufio"
)
type parameterOptions int8
const (
poMandatory parameterOptions = 0x01
poOptional parameterOptions = 0x02
poDefault parameterOptions = 0x04
)
var parameterOptionsText = map[parameterOptions]string{
poMandatory: "mandatory",
poOptional: "optional",
poDefault: "default",
}
func (k parameterOptions) String() string {
t := make([]string, 0, len(parameterOptionsText))
for option, text := range parameterOptionsText {
if (k & option) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
type parameterMode int8
const (
pmIn parameterMode = 0x01
pmInout parameterMode = 0x02
pmOut parameterMode = 0x04
)
var parameterModeText = map[parameterMode]string{
pmIn: "in",
pmInout: "inout",
pmOut: "out",
}
func (k parameterMode) String() string {
t := make([]string, 0, len(parameterModeText))
for mode, text := range parameterModeText {
if (k & mode) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
// ParameterFieldSet contains database field metadata for parameters.
type ParameterFieldSet struct {
fields []*ParameterField
_inputFields []*ParameterField
_outputFields []*ParameterField
names fieldNames
}
func newParameterFieldSet(size int) *ParameterFieldSet {
return &ParameterFieldSet{
fields: make([]*ParameterField, size),
_inputFields: make([]*ParameterField, 0, size),
_outputFields: make([]*ParameterField, 0, size),
names: newFieldNames(),
}
}
// String implements the Stringer interface.
func (f *ParameterFieldSet) String() string {
a := make([]string, len(f.fields))
for i, f := range f.fields {
a[i] = f.String()
}
return fmt.Sprintf("%v", a)
}
func (f *ParameterFieldSet) read(rd *bufio.Reader) {
for i := 0; i < len(f.fields); i++ {
field := newParameterField(f.names)
field.read(rd)
f.fields[i] = field
if field.In() {
f._inputFields = append(f._inputFields, field)
}
if field.Out() {
f._outputFields = append(f._outputFields, field)
}
}
pos := uint32(0)
for _, offset := range f.names.sortOffsets() {
if diff := int(offset - pos); diff > 0 {
rd.Skip(diff)
}
b, size := readShortUtf8(rd)
f.names.setName(offset, string(b))
pos += uint32(1 + size)
}
}
func (f *ParameterFieldSet) inputFields() []*ParameterField {
return f._inputFields
}
func (f *ParameterFieldSet) outputFields() []*ParameterField {
return f._outputFields
}
// NumInputField returns the number of input fields in a database statement.
func (f *ParameterFieldSet) NumInputField() int {
return len(f._inputFields)
}
// NumOutputField returns the number of output fields of a query or stored procedure.
func (f *ParameterFieldSet) NumOutputField() int {
return len(f._outputFields)
}
// Field returns the field at index idx.
func (f *ParameterFieldSet) Field(idx int) *ParameterField {
return f.fields[idx]
}
// OutputField returns the output field at index idx.
func (f *ParameterFieldSet) OutputField(idx int) *ParameterField {
return f._outputFields[idx]
}
// ParameterField contains database field attributes for parameters.
type ParameterField struct {
fieldNames fieldNames
parameterOptions parameterOptions
tc TypeCode
mode parameterMode
fraction int16
length int16
offset uint32
chunkReader lobChunkReader
lobLocatorID locatorID
}
func newParameterField(fieldNames fieldNames) *ParameterField {
return &ParameterField{fieldNames: fieldNames}
}
// String implements the Stringer interface.
func (f *ParameterField) String() string {
return fmt.Sprintf("parameterOptions %s typeCode %s mode %s fraction %d length %d name %s",
f.parameterOptions,
f.tc,
f.mode,
f.fraction,
f.length,
f.Name(),
)
}
// TypeCode returns the type code of the field.
func (f *ParameterField) TypeCode() TypeCode {
return f.tc
}
// TypeLength returns the type length of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength
func (f *ParameterField) TypeLength() (int64, bool) {
if f.tc.isVariableLength() {
return int64(f.length), true
}
return 0, false
}
// TypePrecisionScale returns the type precision and scale (decimal types) of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale
func (f *ParameterField) TypePrecisionScale() (int64, int64, bool) {
if f.tc.isDecimalType() {
return int64(f.length), int64(f.fraction), true
}
return 0, 0, false
}
// Nullable returns true if the field may be null, false otherwise.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable
func (f *ParameterField) Nullable() bool {
return f.parameterOptions == poOptional
}
// In returns true if the parameter field is an input field.
func (f *ParameterField) In() bool {
return f.mode == pmInout || f.mode == pmIn
}
// Out returns true if the parameter field is an output field.
func (f *ParameterField) Out() bool {
return f.mode == pmInout || f.mode == pmOut
}
// Name returns the parameter field name.
func (f *ParameterField) Name() string {
return f.fieldNames.name(f.offset)
}
// SetLobReader sets the io.Reader if a Lob parameter field.
func (f *ParameterField) SetLobReader(rd io.Reader) error {
f.chunkReader = newLobChunkReader(f.TypeCode().isCharBased(), rd)
return nil
}
//
func (f *ParameterField) read(rd *bufio.Reader) {
f.parameterOptions = parameterOptions(rd.ReadInt8())
f.tc = TypeCode(rd.ReadInt8())
f.mode = parameterMode(rd.ReadInt8())
rd.Skip(1) //filler
f.offset = rd.ReadUint32()
f.fieldNames.addOffset(f.offset)
f.length = rd.ReadInt16()
f.fraction = rd.ReadInt16()
rd.Skip(4) //filler
}
// parameter metadata
type parameterMetadata struct {
prmFieldSet *ParameterFieldSet
numArg int
}
func (m *parameterMetadata) String() string {
return fmt.Sprintf("parameter metadata: %s", m.prmFieldSet.fields)
}
func (m *parameterMetadata) kind() partKind {
return pkParameterMetadata
}
func (m *parameterMetadata) setNumArg(numArg int) {
m.numArg = numArg
}
func (m *parameterMetadata) read(rd *bufio.Reader) error {
m.prmFieldSet.read(rd)
if trace {
outLogger.Printf("read %s", m)
}
return rd.GetError()
}
// input parameters
type inputParameters struct {
inputFields []*ParameterField
args []driver.NamedValue
}
func newInputParameters(inputFields []*ParameterField, args []driver.NamedValue) *inputParameters {
return &inputParameters{inputFields: inputFields, args: args}
}
func (p *inputParameters) String() string {
return fmt.Sprintf("input parameters: %v", p.args)
}
func (p *inputParameters) kind() partKind {
return pkParameters
}
func (p *inputParameters) size() (int, error) {
size := len(p.args)
cnt := len(p.inputFields)
for i, arg := range p.args {
if arg.Value == nil { // null value
continue
}
// mass insert
field := p.inputFields[i%cnt]
fieldSize, err := fieldSize(field.TypeCode(), arg)
if err != nil {
return 0, err
}
size += fieldSize
}
return size, nil
}
func (p *inputParameters) numArg() int {
cnt := len(p.inputFields)
if cnt == 0 { // avoid divide-by-zero (e.g. prepare without parameters)
return 0
}
return len(p.args) / cnt
}
func (p *inputParameters) write(wr *bufio.Writer) error {
cnt := len(p.inputFields)
for i, arg := range p.args {
//mass insert
field := p.inputFields[i%cnt]
if err := writeField(wr, field.TypeCode(), arg); err != nil {
return err
}
}
if trace {
outLogger.Printf("input parameters: %s", p)
}
return nil
}
// output parameter
type outputParameters struct {
numArg int
s *Session
outputFields []*ParameterField
fieldValues *FieldValues
}
func (p *outputParameters) String() string {
return fmt.Sprintf("output parameters: %v", p.fieldValues)
}
func (p *outputParameters) kind() partKind {
return pkOutputParameters
}
func (p *outputParameters) setNumArg(numArg int) {
p.numArg = numArg // should always be 1
}
func (p *outputParameters) read(rd *bufio.Reader) error {
cols := len(p.outputFields)
p.fieldValues.resize(p.numArg, cols)
for i := 0; i < p.numArg; i++ {
for j, field := range p.outputFields {
var err error
if p.fieldValues.values[i*cols+j], err = readField(p.s, rd, field.TypeCode()); err != nil {
return err
}
}
}
if trace {
outLogger.Printf("read %s", p)
}
return rd.GetError()
}

View File

@ -1,144 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
partHeaderSize = 16
)
type requestPart interface {
kind() partKind
size() (int, error)
numArg() int
write(*bufio.Writer) error
}
type replyPart interface {
//kind() partKind
setNumArg(int)
read(*bufio.Reader) error
}
// PartAttributes is an interface defining methods for reading query resultset parts.
type PartAttributes interface {
ResultsetClosed() bool
LastPacket() bool
NoRows() bool
}
type partAttributes int8
const (
paLastPacket partAttributes = 0x01
paNextPacket partAttributes = 0x02
paFirstPacket partAttributes = 0x04
paRowNotFound partAttributes = 0x08
paResultsetClosed partAttributes = 0x10
)
var partAttributesText = map[partAttributes]string{
paLastPacket: "lastPacket",
paNextPacket: "nextPacket",
paFirstPacket: "firstPacket",
paRowNotFound: "rowNotFound",
paResultsetClosed: "resultsetClosed",
}
func (k partAttributes) String() string {
t := make([]string, 0, len(partAttributesText))
for attr, text := range partAttributesText {
if (k & attr) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
func (k partAttributes) ResultsetClosed() bool {
return (k & paResultsetClosed) == paResultsetClosed
}
func (k partAttributes) LastPacket() bool {
return (k & paLastPacket) == paLastPacket
}
func (k partAttributes) NoRows() bool {
attrs := paLastPacket | paRowNotFound
return (k & attrs) == attrs
}
// part header
type partHeader struct {
partKind partKind
partAttributes partAttributes
argumentCount int16
bigArgumentCount int32
bufferLength int32
bufferSize int32
}
func (h *partHeader) String() string {
return fmt.Sprintf("part kind %s partAttributes %s argumentCount %d bigArgumentCount %d bufferLength %d bufferSize %d",
h.partKind,
h.partAttributes,
h.argumentCount,
h.bigArgumentCount,
h.bufferLength,
h.bufferSize,
)
}
func (h *partHeader) write(wr *bufio.Writer) error {
wr.WriteInt8(int8(h.partKind))
wr.WriteInt8(int8(h.partAttributes))
wr.WriteInt16(h.argumentCount)
wr.WriteInt32(h.bigArgumentCount)
wr.WriteInt32(h.bufferLength)
wr.WriteInt32(h.bufferSize)
//no filler
if trace {
outLogger.Printf("write part header: %s", h)
}
return nil
}
func (h *partHeader) read(rd *bufio.Reader) error {
h.partKind = partKind(rd.ReadInt8())
h.partAttributes = partAttributes(rd.ReadInt8())
h.argumentCount = rd.ReadInt16()
h.bigArgumentCount = rd.ReadInt32()
h.bufferLength = rd.ReadInt32()
h.bufferSize = rd.ReadInt32()
// no filler
if trace {
outLogger.Printf("read part header: %s", h)
}
return rd.GetError()
}

View File

@ -1,79 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=partKind
type partKind int8
const (
pkNil partKind = 0
pkCommand partKind = 3
pkResultset partKind = 5
pkError partKind = 6
pkStatementID partKind = 10
pkTransactionID partKind = 11
pkRowsAffected partKind = 12
pkResultsetID partKind = 13
pkTopologyInformation partKind = 15
pkTableLocation partKind = 16
pkReadLobRequest partKind = 17
pkReadLobReply partKind = 18
pkAbapIStream partKind = 25
pkAbapOStream partKind = 26
pkCommandInfo partKind = 27
pkWriteLobRequest partKind = 28
pkClientContext partKind = 29
pkWriteLobReply partKind = 30
pkParameters partKind = 32
pkAuthentication partKind = 33
pkSessionContext partKind = 34
pkClientID partKind = 35
pkProfile partKind = 38
pkStatementContext partKind = 39
pkPartitionInformation partKind = 40
pkOutputParameters partKind = 41
pkConnectOptions partKind = 42
pkCommitOptions partKind = 43
pkFetchOptions partKind = 44
pkFetchSize partKind = 45
pkParameterMetadata partKind = 47
pkResultMetadata partKind = 48
pkFindLobRequest partKind = 49
pkFindLobReply partKind = 50
pkItabSHM partKind = 51
pkItabChunkMetadata partKind = 53
pkItabMetadata partKind = 55
pkItabResultChunk partKind = 56
pkClientInfo partKind = 57
pkStreamData partKind = 58
pkOStreamResult partKind = 59
pkFDARequestMetadata partKind = 60
pkFDAReplyMetadata partKind = 61
pkBatchPrepare partKind = 62 //Reserved: do not use
pkBatchExecute partKind = 63 //Reserved: do not use
pkTransactionFlags partKind = 64
pkRowSlotImageParamMetadata partKind = 65 //Reserved: do not use
pkRowSlotImageResultset partKind = 66 //Reserved: do not use
pkDBConnectInfo partKind = 67
pkLobFlags partKind = 68
pkResultsetOptions partKind = 69
pkXATransactionInfo partKind = 70
pkSessionVariable partKind = 71
pkWorkLoadReplayContext partKind = 72
pkSQLReplyOptions partKind = 73
)

View File

@ -1,72 +0,0 @@
// Code generated by "stringer -type=partKind"; DO NOT EDIT.
package protocol
import "strconv"
const _partKind_name = "pkNilpkCommandpkResultsetpkErrorpkStatementIDpkTransactionIDpkRowsAffectedpkResultsetIDpkTopologyInformationpkTableLocationpkReadLobRequestpkReadLobReplypkAbapIStreampkAbapOStreampkCommandInfopkWriteLobRequestpkClientContextpkWriteLobReplypkParameterspkAuthenticationpkSessionContextpkClientIDpkProfilepkStatementContextpkPartitionInformationpkOutputParameterspkConnectOptionspkCommitOptionspkFetchOptionspkFetchSizepkParameterMetadatapkResultMetadatapkFindLobRequestpkFindLobReplypkItabSHMpkItabChunkMetadatapkItabMetadatapkItabResultChunkpkClientInfopkStreamDatapkOStreamResultpkFDARequestMetadatapkFDAReplyMetadatapkBatchPreparepkBatchExecutepkTransactionFlagspkRowSlotImageParamMetadatapkRowSlotImageResultsetpkDBConnectInfopkLobFlagspkResultsetOptionspkXATransactionInfopkSessionVariablepkWorkLoadReplayContextpkSQLReplyOptions"
var _partKind_map = map[partKind]string{
0: _partKind_name[0:5],
3: _partKind_name[5:14],
5: _partKind_name[14:25],
6: _partKind_name[25:32],
10: _partKind_name[32:45],
11: _partKind_name[45:60],
12: _partKind_name[60:74],
13: _partKind_name[74:87],
15: _partKind_name[87:108],
16: _partKind_name[108:123],
17: _partKind_name[123:139],
18: _partKind_name[139:153],
25: _partKind_name[153:166],
26: _partKind_name[166:179],
27: _partKind_name[179:192],
28: _partKind_name[192:209],
29: _partKind_name[209:224],
30: _partKind_name[224:239],
32: _partKind_name[239:251],
33: _partKind_name[251:267],
34: _partKind_name[267:283],
35: _partKind_name[283:293],
38: _partKind_name[293:302],
39: _partKind_name[302:320],
40: _partKind_name[320:342],
41: _partKind_name[342:360],
42: _partKind_name[360:376],
43: _partKind_name[376:391],
44: _partKind_name[391:405],
45: _partKind_name[405:416],
47: _partKind_name[416:435],
48: _partKind_name[435:451],
49: _partKind_name[451:467],
50: _partKind_name[467:481],
51: _partKind_name[481:490],
53: _partKind_name[490:509],
55: _partKind_name[509:523],
56: _partKind_name[523:540],
57: _partKind_name[540:552],
58: _partKind_name[552:564],
59: _partKind_name[564:579],
60: _partKind_name[579:599],
61: _partKind_name[599:617],
62: _partKind_name[617:631],
63: _partKind_name[631:645],
64: _partKind_name[645:663],
65: _partKind_name[663:690],
66: _partKind_name[690:713],
67: _partKind_name[713:728],
68: _partKind_name[728:738],
69: _partKind_name[738:756],
70: _partKind_name[756:775],
71: _partKind_name[775:792],
72: _partKind_name[792:815],
73: _partKind_name[815:832],
}
func (i partKind) String() string {
if str, ok := _partKind_map[i]; ok {
return str
}
return "partKind(" + strconv.FormatInt(int64(i), 10) + ")"
}

View File

@ -1,29 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=QueryType
// QueryType is the type definition for query types supported by this package.
type QueryType byte
// Query type constants.
const (
QtNone QueryType = iota
QtSelect
QtProcedureCall
)

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type=QueryType"; DO NOT EDIT.
package protocol
import "strconv"
const _QueryType_name = "QtNoneQtSelectQtProcedureCall"
var _QueryType_index = [...]uint8{0, 6, 14, 29}
func (i QueryType) String() string {
if i >= QueryType(len(_QueryType_index)-1) {
return "QueryType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _QueryType_name[_QueryType_index[i]:_QueryType_index[i+1]]
}

View File

@ -1,294 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
resultsetIDSize = 8
)
type columnOptions int8
const (
coMandatory columnOptions = 0x01
coOptional columnOptions = 0x02
)
var columnOptionsText = map[columnOptions]string{
coMandatory: "mandatory",
coOptional: "optional",
}
func (k columnOptions) String() string {
t := make([]string, 0, len(columnOptionsText))
for option, text := range columnOptionsText {
if (k & option) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
//resultset id
type resultsetID struct {
id *uint64
}
func (id *resultsetID) kind() partKind {
return pkResultsetID
}
func (id *resultsetID) size() (int, error) {
return resultsetIDSize, nil
}
func (id *resultsetID) numArg() int {
return 1
}
func (id *resultsetID) setNumArg(int) {
//ignore - always 1
}
func (id *resultsetID) read(rd *bufio.Reader) error {
_id := rd.ReadUint64()
*id.id = _id
if trace {
outLogger.Printf("resultset id: %d", *id.id)
}
return rd.GetError()
}
func (id *resultsetID) write(wr *bufio.Writer) error {
wr.WriteUint64(*id.id)
if trace {
outLogger.Printf("resultset id: %d", *id.id)
}
return nil
}
// ResultFieldSet contains database field metadata for result fields.
type ResultFieldSet struct {
fields []*ResultField
names fieldNames
}
func newResultFieldSet(size int) *ResultFieldSet {
return &ResultFieldSet{
fields: make([]*ResultField, size),
names: newFieldNames(),
}
}
// String implements the Stringer interface.
func (f *ResultFieldSet) String() string {
a := make([]string, len(f.fields))
for i, f := range f.fields {
a[i] = f.String()
}
return fmt.Sprintf("%v", a)
}
func (f *ResultFieldSet) read(rd *bufio.Reader) {
for i := 0; i < len(f.fields); i++ {
field := newResultField(f.names)
field.read(rd)
f.fields[i] = field
}
pos := uint32(0)
for _, offset := range f.names.sortOffsets() {
if diff := int(offset - pos); diff > 0 {
rd.Skip(diff)
}
b, size := readShortUtf8(rd)
f.names.setName(offset, string(b))
pos += uint32(1 + size)
}
}
// NumField returns the number of fields of a query.
func (f *ResultFieldSet) NumField() int {
return len(f.fields)
}
// Field returns the field at index idx.
func (f *ResultFieldSet) Field(idx int) *ResultField {
return f.fields[idx]
}
const (
tableName = iota
schemaName
columnName
columnDisplayName
maxNames
)
// ResultField contains database field attributes for result fields.
type ResultField struct {
fieldNames fieldNames
columnOptions columnOptions
tc TypeCode
fraction int16
length int16
offsets [maxNames]uint32
}
func newResultField(fieldNames fieldNames) *ResultField {
return &ResultField{fieldNames: fieldNames}
}
// String implements the Stringer interface.
func (f *ResultField) String() string {
return fmt.Sprintf("columnsOptions %s typeCode %s fraction %d length %d tablename %s schemaname %s columnname %s columnDisplayname %s",
f.columnOptions,
f.tc,
f.fraction,
f.length,
f.fieldNames.name(f.offsets[tableName]),
f.fieldNames.name(f.offsets[schemaName]),
f.fieldNames.name(f.offsets[columnName]),
f.fieldNames.name(f.offsets[columnDisplayName]),
)
}
// TypeCode returns the type code of the field.
func (f *ResultField) TypeCode() TypeCode {
return f.tc
}
// TypeLength returns the type length of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength
func (f *ResultField) TypeLength() (int64, bool) {
if f.tc.isVariableLength() {
return int64(f.length), true
}
return 0, false
}
// TypePrecisionScale returns the type precision and scale (decimal types) of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale
func (f *ResultField) TypePrecisionScale() (int64, int64, bool) {
if f.tc.isDecimalType() {
return int64(f.length), int64(f.fraction), true
}
return 0, 0, false
}
// Nullable returns true if the field may be null, false otherwise.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable
func (f *ResultField) Nullable() bool {
return f.columnOptions == coOptional
}
// Name returns the result field name.
func (f *ResultField) Name() string {
return f.fieldNames.name(f.offsets[columnDisplayName])
}
func (f *ResultField) read(rd *bufio.Reader) {
f.columnOptions = columnOptions(rd.ReadInt8())
f.tc = TypeCode(rd.ReadInt8())
f.fraction = rd.ReadInt16()
f.length = rd.ReadInt16()
rd.Skip(2) //filler
for i := 0; i < maxNames; i++ {
offset := rd.ReadUint32()
f.offsets[i] = offset
f.fieldNames.addOffset(offset)
}
}
//resultset metadata
type resultMetadata struct {
resultFieldSet *ResultFieldSet
numArg int
}
func (r *resultMetadata) String() string {
return fmt.Sprintf("result metadata: %s", r.resultFieldSet.fields)
}
func (r *resultMetadata) kind() partKind {
return pkResultMetadata
}
func (r *resultMetadata) setNumArg(numArg int) {
r.numArg = numArg
}
func (r *resultMetadata) read(rd *bufio.Reader) error {
r.resultFieldSet.read(rd)
if trace {
outLogger.Printf("read %s", r)
}
return rd.GetError()
}
//resultset
type resultset struct {
numArg int
s *Session
resultFieldSet *ResultFieldSet
fieldValues *FieldValues
}
func (r *resultset) String() string {
return fmt.Sprintf("resultset: %s", r.fieldValues)
}
func (r *resultset) kind() partKind {
return pkResultset
}
func (r *resultset) setNumArg(numArg int) {
r.numArg = numArg
}
func (r *resultset) read(rd *bufio.Reader) error {
cols := len(r.resultFieldSet.fields)
r.fieldValues.resize(r.numArg, cols)
for i := 0; i < r.numArg; i++ {
for j, field := range r.resultFieldSet.fields {
var err error
if r.fieldValues.values[i*cols+j], err = readField(r.s, rd, field.TypeCode()); err != nil {
return err
}
}
}
if trace {
outLogger.Printf("read %s", r)
}
return rd.GetError()
}

View File

@ -1,72 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"github.com/SAP/go-hdb/internal/bufio"
)
//rows affected
const (
raSuccessNoInfo = -2
raExecutionFailed = -3
)
//rows affected
type rowsAffected struct {
rows []int32
_numArg int
}
func (r *rowsAffected) kind() partKind {
return pkRowsAffected
}
func (r *rowsAffected) setNumArg(numArg int) {
r._numArg = numArg
}
func (r *rowsAffected) read(rd *bufio.Reader) error {
if r.rows == nil || r._numArg > cap(r.rows) {
r.rows = make([]int32, r._numArg)
} else {
r.rows = r.rows[:r._numArg]
}
for i := 0; i < r._numArg; i++ {
r.rows[i] = rd.ReadInt32()
if trace {
outLogger.Printf("rows affected %d: %d", i, r.rows[i])
}
}
return rd.GetError()
}
func (r *rowsAffected) total() int64 {
if r.rows == nil {
return 0
}
total := int64(0)
for _, rows := range r.rows {
if rows > 0 {
total += int64(rows)
}
}
return total
}

View File

@ -1,265 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//Salted Challenge Response Authentication Mechanism (SCRAM)
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
clientChallengeSize = 64
serverChallengeDataSize = 68
clientProofDataSize = 35
clientProofSize = 32
)
type scramsha256InitialRequest struct {
username []byte
clientChallenge []byte
}
func (r *scramsha256InitialRequest) kind() partKind {
return pkAuthentication
}
func (r *scramsha256InitialRequest) size() (int, error) {
return 2 + authFieldSize(r.username) + authFieldSize([]byte(mnSCRAMSHA256)) + authFieldSize(r.clientChallenge), nil
}
func (r *scramsha256InitialRequest) numArg() int {
return 1
}
func (r *scramsha256InitialRequest) write(wr *bufio.Writer) error {
wr.WriteInt16(3)
writeAuthField(wr, r.username)
writeAuthField(wr, []byte(mnSCRAMSHA256))
writeAuthField(wr, r.clientChallenge)
return nil
}
type scramsha256InitialReply struct {
salt []byte
serverChallenge []byte
}
func (r *scramsha256InitialReply) kind() partKind {
return pkAuthentication
}
func (r *scramsha256InitialReply) setNumArg(int) {
//not needed
}
func (r *scramsha256InitialReply) read(rd *bufio.Reader) error {
cnt := rd.ReadInt16()
if err := readMethodName(rd); err != nil {
return err
}
size := rd.ReadB()
if size != serverChallengeDataSize {
return fmt.Errorf("invalid server challenge data size %d - %d expected", size, serverChallengeDataSize)
}
//server challenge data
cnt = rd.ReadInt16()
if cnt != 2 {
return fmt.Errorf("invalid server challenge data field count %d - %d expected", cnt, 2)
}
size = rd.ReadB()
if trace {
outLogger.Printf("salt size %d", size)
}
r.salt = make([]byte, size)
rd.ReadFull(r.salt)
if trace {
outLogger.Printf("salt %v", r.salt)
}
size = rd.ReadB()
r.serverChallenge = make([]byte, size)
rd.ReadFull(r.serverChallenge)
if trace {
outLogger.Printf("server challenge %v", r.serverChallenge)
}
return rd.GetError()
}
type scramsha256FinalRequest struct {
username []byte
clientProof []byte
}
func newScramsha256FinalRequest() *scramsha256FinalRequest {
return &scramsha256FinalRequest{}
}
func (r *scramsha256FinalRequest) kind() partKind {
return pkAuthentication
}
func (r *scramsha256FinalRequest) size() (int, error) {
return 2 + authFieldSize(r.username) + authFieldSize([]byte(mnSCRAMSHA256)) + authFieldSize(r.clientProof), nil
}
func (r *scramsha256FinalRequest) numArg() int {
return 1
}
func (r *scramsha256FinalRequest) write(wr *bufio.Writer) error {
wr.WriteInt16(3)
writeAuthField(wr, r.username)
writeAuthField(wr, []byte(mnSCRAMSHA256))
writeAuthField(wr, r.clientProof)
return nil
}
type scramsha256FinalReply struct {
serverProof []byte
}
func newScramsha256FinalReply() *scramsha256FinalReply {
return &scramsha256FinalReply{}
}
func (r *scramsha256FinalReply) kind() partKind {
return pkAuthentication
}
func (r *scramsha256FinalReply) setNumArg(int) {
//not needed
}
func (r *scramsha256FinalReply) read(rd *bufio.Reader) error {
cnt := rd.ReadInt16()
if cnt != 2 {
return fmt.Errorf("invalid final reply field count %d - %d expected", cnt, 2)
}
if err := readMethodName(rd); err != nil {
return err
}
//serverProof
size := rd.ReadB()
serverProof := make([]byte, size)
rd.ReadFull(serverProof)
return rd.GetError()
}
//helper
func authFieldSize(f []byte) int {
size := len(f)
if size >= 250 {
// - different indicators compared to db field handling
// - 1-5 bytes? but only 1 resp 3 bytes explained
panic("not implemented error")
}
return size + 1 //length indicator size := 1
}
func writeAuthField(wr *bufio.Writer, f []byte) {
size := len(f)
if size >= 250 {
// - different indicators compared to db field handling
// - 1-5 bytes? but only 1 resp 3 bytes explained
panic("not implemented error")
}
wr.WriteB(byte(size))
wr.Write(f)
}
func readMethodName(rd *bufio.Reader) error {
size := rd.ReadB()
methodName := make([]byte, size)
rd.ReadFull(methodName)
if string(methodName) != mnSCRAMSHA256 {
return fmt.Errorf("invalid authentication method %s - %s expected", methodName, mnSCRAMSHA256)
}
return nil
}
func clientChallenge() []byte {
r := make([]byte, clientChallengeSize)
if _, err := rand.Read(r); err != nil {
outLogger.Fatal("client challenge fatal error")
}
return r
}
func clientProof(salt, serverChallenge, clientChallenge, password []byte) []byte {
clientProof := make([]byte, clientProofDataSize)
buf := make([]byte, 0, len(salt)+len(serverChallenge)+len(clientChallenge))
buf = append(buf, salt...)
buf = append(buf, serverChallenge...)
buf = append(buf, clientChallenge...)
key := _sha256(_hmac(password, salt))
sig := _hmac(_sha256(key), buf)
proof := xor(sig, key)
//actual implementation: only one salt value?
clientProof[0] = 0
clientProof[1] = 1
clientProof[2] = clientProofSize
copy(clientProof[3:], proof)
return clientProof
}
func _sha256(p []byte) []byte {
hash := sha256.New()
hash.Write(p)
s := hash.Sum(nil)
if trace {
outLogger.Printf("sha length %d value %v", len(s), s)
}
return s
}
func _hmac(key, p []byte) []byte {
hash := hmac.New(sha256.New, key)
hash.Write(p)
s := hash.Sum(nil)
if trace {
outLogger.Printf("hmac length %d value %v", len(s), s)
}
return s
}
func xor(sig, key []byte) []byte {
r := make([]byte, len(sig))
for i, v := range sig {
r[i] = v ^ key[i]
}
return r
}

View File

@ -1,174 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
const (
segmentHeaderSize = 24
)
type commandOptions int8
const (
coNil commandOptions = 0x00
coSelfetchOff commandOptions = 0x01
coScrollableCursorOn commandOptions = 0x02
coNoResultsetCloseNeeded commandOptions = 0x04
coHoldCursorOverCommtit commandOptions = 0x08
coExecuteLocally commandOptions = 0x10
)
var commandOptionsText = map[commandOptions]string{
coSelfetchOff: "selfetchOff",
coScrollableCursorOn: "scrollabeCursorOn",
coNoResultsetCloseNeeded: "noResltsetCloseNeeded",
coHoldCursorOverCommtit: "holdCursorOverCommit",
coExecuteLocally: "executLocally",
}
func (k commandOptions) String() string {
t := make([]string, 0, len(commandOptionsText))
for option, text := range commandOptionsText {
if (k & option) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
//segment header
type segmentHeader struct {
segmentLength int32
segmentOfs int32
noOfParts int16
segmentNo int16
segmentKind segmentKind
messageType messageType
commit bool
commandOptions commandOptions
functionCode functionCode
}
func (h *segmentHeader) String() string {
switch h.segmentKind {
default: //error
return fmt.Sprintf(
"segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s",
h.segmentLength,
h.segmentOfs,
h.noOfParts,
h.segmentNo,
h.segmentKind,
)
case skRequest:
return fmt.Sprintf(
"segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s messageType %s commit %t commandOptions %s",
h.segmentLength,
h.segmentOfs,
h.noOfParts,
h.segmentNo,
h.segmentKind,
h.messageType,
h.commit,
h.commandOptions,
)
case skReply:
return fmt.Sprintf(
"segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s functionCode %s",
h.segmentLength,
h.segmentOfs,
h.noOfParts,
h.segmentNo,
h.segmentKind,
h.functionCode,
)
}
}
// request
func (h *segmentHeader) write(wr *bufio.Writer) error {
wr.WriteInt32(h.segmentLength)
wr.WriteInt32(h.segmentOfs)
wr.WriteInt16(h.noOfParts)
wr.WriteInt16(h.segmentNo)
wr.WriteInt8(int8(h.segmentKind))
switch h.segmentKind {
default: //error
wr.WriteZeroes(11) //segmentHeaderLength
case skRequest:
wr.WriteInt8(int8(h.messageType))
wr.WriteBool(h.commit)
wr.WriteInt8(int8(h.commandOptions))
wr.WriteZeroes(8) //segmentHeaderSize
case skReply:
wr.WriteZeroes(1) //reserved
wr.WriteInt16(int16(h.functionCode))
wr.WriteZeroes(8) //segmentHeaderSize
}
if trace {
outLogger.Printf("write segment header: %s", h)
}
return nil
}
// reply || error
func (h *segmentHeader) read(rd *bufio.Reader) error {
h.segmentLength = rd.ReadInt32()
h.segmentOfs = rd.ReadInt32()
h.noOfParts = rd.ReadInt16()
h.segmentNo = rd.ReadInt16()
h.segmentKind = segmentKind(rd.ReadInt8())
switch h.segmentKind {
default: //error
rd.Skip(11) //segmentHeaderLength
case skRequest:
h.messageType = messageType(rd.ReadInt8())
h.commit = rd.ReadBool()
h.commandOptions = commandOptions(rd.ReadInt8())
rd.Skip(8) //segmentHeaderLength
case skReply:
rd.Skip(1) //reserved
h.functionCode = functionCode(rd.ReadInt16())
rd.Skip(8) //segmentHeaderLength
}
if trace {
outLogger.Printf("read segment header: %s", h)
}
return rd.GetError()
}

View File

@ -1,28 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=segmentKind
type segmentKind int8
const (
skInvalid segmentKind = 0
skRequest segmentKind = 1
skReply segmentKind = 2
skError segmentKind = 5
)

View File

@ -1,25 +0,0 @@
// Code generated by "stringer -type=segmentKind"; DO NOT EDIT.
package protocol
import "strconv"
const (
_segmentKind_name_0 = "skInvalidskRequestskReply"
_segmentKind_name_1 = "skError"
)
var (
_segmentKind_index_0 = [...]uint8{0, 9, 18, 25}
)
func (i segmentKind) String() string {
switch {
case 0 <= i && i <= 2:
return _segmentKind_name_0[_segmentKind_index_0[i]:_segmentKind_index_0[i+1]]
case i == 5:
return _segmentKind_name_1
default:
return "segmentKind(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@ -1,975 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"context"
"crypto/tls"
"database/sql/driver"
"flag"
"fmt"
"log"
"math"
"net"
"os"
"sync"
"time"
"github.com/SAP/go-hdb/internal/bufio"
"github.com/SAP/go-hdb/internal/unicode"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
"github.com/SAP/go-hdb/driver/sqltrace"
)
const (
mnSCRAMSHA256 = "SCRAMSHA256"
mnGSS = "GSS"
mnSAML = "SAML"
)
var trace bool
func init() {
flag.BoolVar(&trace, "hdb.protocol.trace", false, "enabling hdb protocol trace")
}
var (
outLogger = log.New(os.Stdout, "hdb.protocol ", log.Ldate|log.Ltime|log.Lshortfile)
errLogger = log.New(os.Stderr, "hdb.protocol ", log.Ldate|log.Ltime|log.Lshortfile)
)
//padding
const (
padding = 8
)
func padBytes(size int) int {
if r := size % padding; r != 0 {
return padding - r
}
return 0
}
// SessionConn wraps the database tcp connection. It sets timeouts and handles driver ErrBadConn behavior.
type sessionConn struct {
addr string
timeout time.Duration
conn net.Conn
isBad bool // bad connection
badError error // error cause for session bad state
inTx bool // in transaction
}
func newSessionConn(ctx context.Context, addr string, timeoutSec int, config *tls.Config) (*sessionConn, error) {
timeout := time.Duration(timeoutSec) * time.Second
dialer := net.Dialer{Timeout: timeout}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
}
// is TLS connection requested?
if config != nil {
conn = tls.Client(conn, config)
}
return &sessionConn{addr: addr, timeout: timeout, conn: conn}, nil
}
func (c *sessionConn) close() error {
return c.conn.Close()
}
// Read implements the io.Reader interface.
func (c *sessionConn) Read(b []byte) (int, error) {
//set timeout
if err := c.conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil {
return 0, err
}
n, err := c.conn.Read(b)
if err != nil {
errLogger.Printf("Connection read error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err)
c.isBad = true
c.badError = err
return n, driver.ErrBadConn
}
return n, nil
}
// Write implements the io.Writer interface.
func (c *sessionConn) Write(b []byte) (int, error) {
//set timeout
if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil {
return 0, err
}
n, err := c.conn.Write(b)
if err != nil {
errLogger.Printf("Connection write error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err)
c.isBad = true
c.badError = err
return n, driver.ErrBadConn
}
return n, nil
}
type beforeRead func(p replyPart)
// session parameter
type sessionPrm interface {
Host() string
Username() string
Password() string
Locale() string
FetchSize() int
Timeout() int
TLSConfig() *tls.Config
}
// Session represents a HDB session.
type Session struct {
prm sessionPrm
conn *sessionConn
rd *bufio.Reader
wr *bufio.Writer
// reuse header
mh *messageHeader
sh *segmentHeader
ph *partHeader
//reuse request / reply parts
scramsha256InitialRequest *scramsha256InitialRequest
scramsha256InitialReply *scramsha256InitialReply
scramsha256FinalRequest *scramsha256FinalRequest
scramsha256FinalReply *scramsha256FinalReply
topologyInformation *topologyInformation
connectOptions *connectOptions
rowsAffected *rowsAffected
statementID *statementID
resultMetadata *resultMetadata
resultsetID *resultsetID
resultset *resultset
parameterMetadata *parameterMetadata
outputParameters *outputParameters
writeLobRequest *writeLobRequest
readLobRequest *readLobRequest
writeLobReply *writeLobReply
readLobReply *readLobReply
//standard replies
stmtCtx *statementContext
txFlags *transactionFlags
lastError *hdbErrors
//serialize write request - read reply
//supports calling session methods in go routines (driver methods with context cancellation)
mu sync.Mutex
}
// NewSession creates a new database session.
func NewSession(ctx context.Context, prm sessionPrm) (*Session, error) {
if trace {
outLogger.Printf("%s", prm)
}
conn, err := newSessionConn(ctx, prm.Host(), prm.Timeout(), prm.TLSConfig())
if err != nil {
return nil, err
}
rd := bufio.NewReader(conn)
wr := bufio.NewWriter(conn)
s := &Session{
prm: prm,
conn: conn,
rd: rd,
wr: wr,
mh: new(messageHeader),
sh: new(segmentHeader),
ph: new(partHeader),
scramsha256InitialRequest: new(scramsha256InitialRequest),
scramsha256InitialReply: new(scramsha256InitialReply),
scramsha256FinalRequest: new(scramsha256FinalRequest),
scramsha256FinalReply: new(scramsha256FinalReply),
topologyInformation: newTopologyInformation(),
connectOptions: newConnectOptions(),
rowsAffected: new(rowsAffected),
statementID: new(statementID),
resultMetadata: new(resultMetadata),
resultsetID: new(resultsetID),
resultset: new(resultset),
parameterMetadata: new(parameterMetadata),
outputParameters: new(outputParameters),
writeLobRequest: new(writeLobRequest),
readLobRequest: new(readLobRequest),
writeLobReply: new(writeLobReply),
readLobReply: new(readLobReply),
stmtCtx: newStatementContext(),
txFlags: newTransactionFlags(),
lastError: new(hdbErrors),
}
if err = s.init(); err != nil {
return nil, err
}
return s, nil
}
// Close closes the session.
func (s *Session) Close() error {
return s.conn.close()
}
func (s *Session) sessionID() int64 {
return s.mh.sessionID
}
// InTx indicates, if the session is in transaction mode.
func (s *Session) InTx() bool {
return s.conn.inTx
}
// SetInTx sets session in transaction mode.
func (s *Session) SetInTx(v bool) {
s.conn.inTx = v
}
// IsBad indicates, that the session is in bad state.
func (s *Session) IsBad() bool {
return s.conn.isBad
}
// BadErr returns the error, that caused the bad session state.
func (s *Session) BadErr() error {
return s.conn.badError
}
func (s *Session) init() error {
if err := s.initRequest(); err != nil {
return err
}
// TODO: detect authentication method
// - actually only basic authetication supported
authentication := mnSCRAMSHA256
switch authentication {
default:
return fmt.Errorf("invalid authentication %s", authentication)
case mnSCRAMSHA256:
if err := s.authenticateScramsha256(); err != nil {
return err
}
case mnGSS:
panic("not implemented error")
case mnSAML:
panic("not implemented error")
}
id := s.sessionID()
if id <= 0 {
return fmt.Errorf("invalid session id %d", id)
}
if trace {
outLogger.Printf("sessionId %d", id)
}
return nil
}
func (s *Session) authenticateScramsha256() error {
tr := unicode.Utf8ToCesu8Transformer
tr.Reset()
username := make([]byte, cesu8.StringSize(s.prm.Username()))
if _, _, err := tr.Transform(username, []byte(s.prm.Username()), true); err != nil {
return err // should never happen
}
password := make([]byte, cesu8.StringSize(s.prm.Password()))
if _, _, err := tr.Transform(password, []byte(s.prm.Password()), true); err != nil {
return err //should never happen
}
clientChallenge := clientChallenge()
//initial request
s.scramsha256InitialRequest.username = username
s.scramsha256InitialRequest.clientChallenge = clientChallenge
if err := s.writeRequest(mtAuthenticate, false, s.scramsha256InitialRequest); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
//final request
s.scramsha256FinalRequest.username = username
s.scramsha256FinalRequest.clientProof = clientProof(s.scramsha256InitialReply.salt, s.scramsha256InitialReply.serverChallenge, clientChallenge, password)
s.scramsha256InitialReply = nil // !!! next time readReply uses FinalReply
id := newClientID()
co := newConnectOptions()
co.set(coDistributionProtocolVersion, booleanType(false))
co.set(coSelectForUpdateSupported, booleanType(false))
co.set(coSplitBatchCommands, booleanType(true))
// cannot use due to HDB protocol error with secondtime datatype
//co.set(coDataFormatVersion2, dfvSPS06)
co.set(coDataFormatVersion2, dfvBaseline)
co.set(coCompleteArrayExecution, booleanType(true))
if s.prm.Locale() != "" {
co.set(coClientLocale, stringType(s.prm.Locale()))
}
co.set(coClientDistributionMode, cdmOff)
// setting this option has no effect
//co.set(coImplicitLobStreaming, booleanType(true))
if err := s.writeRequest(mtConnect, false, s.scramsha256FinalRequest, id, co); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
return nil
}
// QueryDirect executes a query without query parameters.
func (s *Session) QueryDirect(query string) (uint64, *ResultFieldSet, *FieldValues, PartAttributes, error) {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.writeRequest(mtExecuteDirect, false, command(query)); err != nil {
return 0, nil, nil, nil, err
}
var id uint64
var resultFieldSet *ResultFieldSet
fieldValues := newFieldValues()
f := func(p replyPart) {
switch p := p.(type) {
case *resultsetID:
p.id = &id
case *resultMetadata:
resultFieldSet = newResultFieldSet(p.numArg)
p.resultFieldSet = resultFieldSet
case *resultset:
p.s = s
p.resultFieldSet = resultFieldSet
p.fieldValues = fieldValues
}
}
if err := s.readReply(f); err != nil {
return 0, nil, nil, nil, err
}
return id, resultFieldSet, fieldValues, s.ph.partAttributes, nil
}
// ExecDirect executes a sql statement without statement parameters.
func (s *Session) ExecDirect(query string) (driver.Result, error) {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.writeRequest(mtExecuteDirect, !s.conn.inTx, command(query)); err != nil {
return nil, err
}
if err := s.readReply(nil); err != nil {
return nil, err
}
if s.sh.functionCode == fcDDL {
return driver.ResultNoRows, nil
}
return driver.RowsAffected(s.rowsAffected.total()), nil
}
// Prepare prepares a sql statement.
func (s *Session) Prepare(query string) (QueryType, uint64, *ParameterFieldSet, *ResultFieldSet, error) {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.writeRequest(mtPrepare, false, command(query)); err != nil {
return QtNone, 0, nil, nil, err
}
var id uint64
var prmFieldSet *ParameterFieldSet
var resultFieldSet *ResultFieldSet
f := func(p replyPart) {
switch p := p.(type) {
case *statementID:
p.id = &id
case *parameterMetadata:
prmFieldSet = newParameterFieldSet(p.numArg)
p.prmFieldSet = prmFieldSet
case *resultMetadata:
resultFieldSet = newResultFieldSet(p.numArg)
p.resultFieldSet = resultFieldSet
}
}
if err := s.readReply(f); err != nil {
return QtNone, 0, nil, nil, err
}
return s.sh.functionCode.queryType(), id, prmFieldSet, resultFieldSet, nil
}
// Exec executes a sql statement.
func (s *Session) Exec(id uint64, prmFieldSet *ParameterFieldSet, args []driver.NamedValue) (driver.Result, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.statementID.id = &id
if err := s.writeRequest(mtExecute, !s.conn.inTx, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil {
return nil, err
}
if err := s.readReply(nil); err != nil {
return nil, err
}
var result driver.Result
if s.sh.functionCode == fcDDL {
result = driver.ResultNoRows
} else {
result = driver.RowsAffected(s.rowsAffected.total())
}
if err := s.writeLobStream(prmFieldSet, nil, args); err != nil {
return nil, err
}
return result, nil
}
// DropStatementID releases the hdb statement handle.
func (s *Session) DropStatementID(id uint64) error {
s.mu.Lock()
defer s.mu.Unlock()
s.statementID.id = &id
if err := s.writeRequest(mtDropStatementID, false, s.statementID); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
return nil
}
// Call executes a stored procedure.
func (s *Session) Call(id uint64, prmFieldSet *ParameterFieldSet, args []driver.NamedValue) (*FieldValues, []*TableResult, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.statementID.id = &id
if err := s.writeRequest(mtExecute, false, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil {
return nil, nil, err
}
prmFieldValues := newFieldValues()
var tableResults []*TableResult
var tableResult *TableResult
f := func(p replyPart) {
switch p := p.(type) {
case *outputParameters:
p.s = s
p.outputFields = prmFieldSet.outputFields()
p.fieldValues = prmFieldValues
// table output parameters: meta, id, result (only first param?)
case *resultMetadata:
tableResult = newTableResult(s, p.numArg)
tableResults = append(tableResults, tableResult)
p.resultFieldSet = tableResult.resultFieldSet
case *resultsetID:
p.id = &(tableResult.id)
case *resultset:
p.s = s
tableResult.attrs = s.ph.partAttributes
p.resultFieldSet = tableResult.resultFieldSet
p.fieldValues = tableResult.fieldValues
}
}
if err := s.readReply(f); err != nil {
return nil, nil, err
}
if err := s.writeLobStream(prmFieldSet, prmFieldValues, args); err != nil {
return nil, nil, err
}
return prmFieldValues, tableResults, nil
}
// Query executes a query.
func (s *Session) Query(stmtID uint64, prmFieldSet *ParameterFieldSet, resultFieldSet *ResultFieldSet, args []driver.NamedValue) (uint64, *FieldValues, PartAttributes, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.statementID.id = &stmtID
if err := s.writeRequest(mtExecute, false, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil {
return 0, nil, nil, err
}
var rsetID uint64
fieldValues := newFieldValues()
f := func(p replyPart) {
switch p := p.(type) {
case *resultsetID:
p.id = &rsetID
case *resultset:
p.s = s
p.resultFieldSet = resultFieldSet
p.fieldValues = fieldValues
}
}
if err := s.readReply(f); err != nil {
return 0, nil, nil, err
}
return rsetID, fieldValues, s.ph.partAttributes, nil
}
// FetchNext fetches next chunk in query result set.
func (s *Session) FetchNext(id uint64, resultFieldSet *ResultFieldSet, fieldValues *FieldValues) (PartAttributes, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.resultsetID.id = &id
if err := s.writeRequest(mtFetchNext, false, s.resultsetID, fetchsize(s.prm.FetchSize())); err != nil {
return nil, err
}
f := func(p replyPart) {
switch p := p.(type) {
case *resultset:
p.s = s
p.resultFieldSet = resultFieldSet
p.fieldValues = fieldValues
}
}
if err := s.readReply(f); err != nil {
return nil, err
}
return s.ph.partAttributes, nil
}
// CloseResultsetID releases the hdb resultset handle.
func (s *Session) CloseResultsetID(id uint64) error {
s.mu.Lock()
defer s.mu.Unlock()
s.resultsetID.id = &id
if err := s.writeRequest(mtCloseResultset, false, s.resultsetID); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
return nil
}
// Commit executes a database commit.
func (s *Session) Commit() error {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.writeRequest(mtCommit, false); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
if trace {
outLogger.Printf("transaction flags: %s", s.txFlags)
}
s.conn.inTx = false
return nil
}
// Rollback executes a database rollback.
func (s *Session) Rollback() error {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.writeRequest(mtRollback, false); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
if trace {
outLogger.Printf("transaction flags: %s", s.txFlags)
}
s.conn.inTx = false
return nil
}
//
func (s *Session) readLobStream(w lobChunkWriter) error {
s.readLobRequest.w = w
s.readLobReply.w = w
for !w.eof() {
if err := s.writeRequest(mtWriteLob, false, s.readLobRequest); err != nil {
return err
}
if err := s.readReply(nil); err != nil {
return err
}
}
return nil
}
func (s *Session) writeLobStream(prmFieldSet *ParameterFieldSet, prmFieldValues *FieldValues, args []driver.NamedValue) error {
if s.writeLobReply.numArg == 0 {
return nil
}
lobPrmFields := make([]*ParameterField, s.writeLobReply.numArg)
j := 0
for _, f := range prmFieldSet.fields {
if f.TypeCode().isLob() && f.In() && f.chunkReader != nil {
f.lobLocatorID = s.writeLobReply.ids[j]
lobPrmFields[j] = f
j++
}
}
if j != s.writeLobReply.numArg {
return fmt.Errorf("protocol error: invalid number of lob parameter ids %d - expected %d", j, s.writeLobReply.numArg)
}
s.writeLobRequest.lobPrmFields = lobPrmFields
f := func(p replyPart) {
if p, ok := p.(*outputParameters); ok {
p.s = s
p.outputFields = prmFieldSet.outputFields()
p.fieldValues = prmFieldValues
}
}
for s.writeLobReply.numArg != 0 {
if err := s.writeRequest(mtReadLob, false, s.writeLobRequest); err != nil {
return err
}
if err := s.readReply(f); err != nil {
return err
}
}
return nil
}
//
func (s *Session) initRequest() error {
// init
s.mh.sessionID = -1
// handshake
req := newInitRequest()
// TODO: constants
req.product.major = 4
req.product.minor = 20
req.protocol.major = 4
req.protocol.minor = 1
req.numOptions = 1
req.endianess = archEndian
if err := req.write(s.wr); err != nil {
return err
}
rep := newInitReply()
if err := rep.read(s.rd); err != nil {
return err
}
return nil
}
func (s *Session) writeRequest(messageType messageType, commit bool, requests ...requestPart) error {
partSize := make([]int, len(requests))
size := int64(segmentHeaderSize + len(requests)*partHeaderSize) //int64 to hold MaxUInt32 in 32bit OS
for i, part := range requests {
s, err := part.size()
if err != nil {
return err
}
size += int64(s + padBytes(s))
partSize[i] = s // buffer size (expensive calculation)
}
if size > math.MaxUint32 {
return fmt.Errorf("message size %d exceeds maximum message header value %d", size, int64(math.MaxUint32)) //int64: without cast overflow error in 32bit OS
}
bufferSize := size
s.mh.varPartLength = uint32(size)
s.mh.varPartSize = uint32(bufferSize)
s.mh.noOfSegm = 1
if err := s.mh.write(s.wr); err != nil {
return err
}
if size > math.MaxInt32 {
return fmt.Errorf("message size %d exceeds maximum part header value %d", size, math.MaxInt32)
}
s.sh.messageType = messageType
s.sh.commit = commit
s.sh.segmentKind = skRequest
s.sh.segmentLength = int32(size)
s.sh.segmentOfs = 0
s.sh.noOfParts = int16(len(requests))
s.sh.segmentNo = 1
if err := s.sh.write(s.wr); err != nil {
return err
}
bufferSize -= segmentHeaderSize
for i, part := range requests {
size := partSize[i]
pad := padBytes(size)
s.ph.partKind = part.kind()
numArg := part.numArg()
switch {
default:
return fmt.Errorf("maximum number of arguments %d exceeded", numArg)
case numArg <= math.MaxInt16:
s.ph.argumentCount = int16(numArg)
s.ph.bigArgumentCount = 0
// TODO: seems not to work: see bulk insert test
case numArg <= math.MaxInt32:
s.ph.argumentCount = 0
s.ph.bigArgumentCount = int32(numArg)
}
s.ph.bufferLength = int32(size)
s.ph.bufferSize = int32(bufferSize)
if err := s.ph.write(s.wr); err != nil {
return err
}
if err := part.write(s.wr); err != nil {
return err
}
s.wr.WriteZeroes(pad)
bufferSize -= int64(partHeaderSize + size + pad)
}
return s.wr.Flush()
}
func (s *Session) readReply(beforeRead beforeRead) error {
replyRowsAffected := false
replyError := false
if err := s.mh.read(s.rd); err != nil {
return err
}
if s.mh.noOfSegm != 1 {
return fmt.Errorf("simple message: no of segments %d - expected 1", s.mh.noOfSegm)
}
if err := s.sh.read(s.rd); err != nil {
return err
}
// TODO: protocol error (sps 82)?: message header varPartLength < segment header segmentLength (*1)
diff := int(s.mh.varPartLength) - int(s.sh.segmentLength)
if trace && diff != 0 {
outLogger.Printf("+++++diff %d", diff)
}
noOfParts := int(s.sh.noOfParts)
lastPart := noOfParts - 1
for i := 0; i < noOfParts; i++ {
if err := s.ph.read(s.rd); err != nil {
return err
}
numArg := int(s.ph.argumentCount)
var part replyPart
switch s.ph.partKind {
case pkAuthentication:
if s.scramsha256InitialReply != nil { // first call: initial reply
part = s.scramsha256InitialReply
} else { // second call: final reply
part = s.scramsha256FinalReply
}
case pkTopologyInformation:
part = s.topologyInformation
case pkConnectOptions:
part = s.connectOptions
case pkStatementID:
part = s.statementID
case pkResultMetadata:
part = s.resultMetadata
case pkResultsetID:
part = s.resultsetID
case pkResultset:
part = s.resultset
case pkParameterMetadata:
part = s.parameterMetadata
case pkOutputParameters:
part = s.outputParameters
case pkError:
replyError = true
part = s.lastError
case pkStatementContext:
part = s.stmtCtx
case pkTransactionFlags:
part = s.txFlags
case pkRowsAffected:
replyRowsAffected = true
part = s.rowsAffected
case pkReadLobReply:
part = s.readLobReply
case pkWriteLobReply:
part = s.writeLobReply
default:
return fmt.Errorf("read not expected part kind %s", s.ph.partKind)
}
part.setNumArg(numArg)
if beforeRead != nil {
beforeRead(part)
}
if err := part.read(s.rd); err != nil {
return err
}
if i != lastPart { // not last part
// Error padding (protocol error?)
// driver test TestHDBWarning
// --> 18 bytes fix error bytes + 103 bytes error text => 121 bytes (7 bytes padding needed)
// but s.ph.bufferLength = 122 (standard padding would only consume 6 bytes instead of 7)
// driver test TestBulkInsertDuplicates
// --> returns 3 errors (number of total bytes matches s.ph.bufferLength)
// ==> hdbErrors take care about padding
if s.ph.partKind != pkError {
s.rd.Skip(padBytes(int(s.ph.bufferLength)))
}
}
}
// last part
// TODO: workaround (see *)
if diff == 0 {
s.rd.Skip(padBytes(int(s.ph.bufferLength)))
}
if err := s.rd.GetError(); err != nil {
return err
}
if replyError {
if replyRowsAffected { //link statement to error
j := 0
for i, rows := range s.rowsAffected.rows {
if rows == raExecutionFailed {
s.lastError.setStmtNo(j, i)
j++
}
}
}
if s.lastError.isWarnings() {
for _, _error := range s.lastError.errors {
sqltrace.Traceln(_error)
}
return nil
}
return s.lastError
}
return nil
}

View File

@ -1,203 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"log"
"net"
"github.com/SAP/go-hdb/internal/bufio"
)
type dir bool
const (
maxBinarySize = 128
)
type fragment interface {
read(rd *bufio.Reader) error
write(wr *bufio.Writer) error
}
func (d dir) String() string {
if d {
return "->"
}
return "<-"
}
// A Sniffer is a simple proxy for logging hdb protocol requests and responses.
type Sniffer struct {
conn net.Conn
dbAddr string
dbConn net.Conn
//client
clRd *bufio.Reader
clWr *bufio.Writer
//database
dbRd *bufio.Reader
dbWr *bufio.Writer
mh *messageHeader
sh *segmentHeader
ph *partHeader
buf []byte
}
// NewSniffer creates a new sniffer instance. The conn parameter is the net.Conn connection, where the Sniffer
// is listening for hdb protocol calls. The dbAddr is the hdb host port address in "host:port" format.
func NewSniffer(conn net.Conn, dbAddr string) (*Sniffer, error) {
s := &Sniffer{
conn: conn,
dbAddr: dbAddr,
clRd: bufio.NewReader(conn),
clWr: bufio.NewWriter(conn),
mh: &messageHeader{},
sh: &segmentHeader{},
ph: &partHeader{},
buf: make([]byte, 0),
}
dbConn, err := net.Dial("tcp", s.dbAddr)
if err != nil {
return nil, err
}
s.dbRd = bufio.NewReader(dbConn)
s.dbWr = bufio.NewWriter(dbConn)
s.dbConn = dbConn
return s, nil
}
func (s *Sniffer) getBuffer(size int) []byte {
if cap(s.buf) < size {
s.buf = make([]byte, size)
}
return s.buf[:size]
}
// Go starts the protocol request and response logging.
func (s *Sniffer) Go() {
defer s.dbConn.Close()
defer s.conn.Close()
req := newInitRequest()
if err := s.streamFragment(dir(true), s.clRd, s.dbWr, req); err != nil {
return
}
rep := newInitReply()
if err := s.streamFragment(dir(false), s.dbRd, s.clWr, rep); err != nil {
return
}
for {
//up stream
if err := s.stream(dir(true), s.clRd, s.dbWr); err != nil {
return
}
//down stream
if err := s.stream(dir(false), s.dbRd, s.clWr); err != nil {
return
}
}
}
func (s *Sniffer) stream(d dir, from *bufio.Reader, to *bufio.Writer) error {
if err := s.streamFragment(d, from, to, s.mh); err != nil {
return err
}
size := int(s.mh.varPartLength)
for i := 0; i < int(s.mh.noOfSegm); i++ {
if err := s.streamFragment(d, from, to, s.sh); err != nil {
return err
}
size -= int(s.sh.segmentLength)
for j := 0; j < int(s.sh.noOfParts); j++ {
if err := s.streamFragment(d, from, to, s.ph); err != nil {
return err
}
// protocol error workaraound
padding := (size == 0) || (j != (int(s.sh.noOfParts) - 1))
if err := s.streamPart(d, from, to, s.ph, padding); err != nil {
return err
}
}
}
return to.Flush()
}
func (s *Sniffer) streamPart(d dir, from *bufio.Reader, to *bufio.Writer, ph *partHeader, padding bool) error {
switch ph.partKind {
default:
return s.streamBinary(d, from, to, int(ph.bufferLength), padding)
}
}
func (s *Sniffer) streamBinary(d dir, from *bufio.Reader, to *bufio.Writer, size int, padding bool) error {
var b []byte
//protocol error workaraound
if padding {
pad := padBytes(size)
b = s.getBuffer(size + pad)
} else {
b = s.getBuffer(size)
}
from.ReadFull(b)
err := from.GetError()
if err != nil {
log.Print(err)
return err
}
if size > maxBinarySize {
log.Printf("%s %v", d, b[:maxBinarySize])
} else {
log.Printf("%s %v", d, b[:size])
}
to.Write(b)
return nil
}
func (s *Sniffer) streamFragment(d dir, from *bufio.Reader, to *bufio.Writer, f fragment) error {
if err := f.read(from); err != nil {
log.Print(err)
return err
}
log.Printf("%s %s", d, f)
if err := f.write(to); err != nil {
log.Print(err)
return err
}
return nil
}

View File

@ -1,60 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
type statementContext struct {
options plainOptions
_numArg int
}
func newStatementContext() *statementContext {
return &statementContext{
options: plainOptions{},
}
}
func (c *statementContext) String() string {
typedSc := make(map[statementContextType]interface{})
for k, v := range c.options {
typedSc[statementContextType(k)] = v
}
return fmt.Sprintf("%s", typedSc)
}
func (c *statementContext) kind() partKind {
return pkStatementContext
}
func (c *statementContext) setNumArg(numArg int) {
c._numArg = numArg
}
func (c *statementContext) read(rd *bufio.Reader) error {
c.options.read(rd, c._numArg)
if trace {
outLogger.Printf("statement context: %v", c)
}
return rd.GetError()
}

View File

@ -1,26 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=statementContextType
type statementContextType int8
const (
scStatementSequenceInfo statementContextType = 1
scServerExecutionTime statementContextType = 2
)

View File

@ -1,17 +0,0 @@
// Code generated by "stringer -type=statementContextType"; DO NOT EDIT.
package protocol
import "strconv"
const _statementContextType_name = "scStatementSequenceInfoscServerExecutionTime"
var _statementContextType_index = [...]uint8{0, 23, 44}
func (i statementContextType) String() string {
i -= 1
if i < 0 || i >= statementContextType(len(_statementContextType_index)-1) {
return "statementContextType(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _statementContextType_name[_statementContextType_index[i]:_statementContextType_index[i+1]]
}

View File

@ -1,66 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"github.com/SAP/go-hdb/internal/bufio"
)
const (
statementIDSize = 8
)
type statementID struct {
id *uint64
}
func (id statementID) kind() partKind {
return pkStatementID
}
func (id statementID) size() (int, error) {
return statementIDSize, nil
}
func (id statementID) numArg() int {
return 1
}
func (id statementID) setNumArg(int) {
//ignore - always 1
}
func (id *statementID) read(rd *bufio.Reader) error {
_id := rd.ReadUint64()
*id.id = _id
if trace {
outLogger.Printf("statement id: %d", *id.id)
}
return rd.GetError()
}
func (id statementID) write(wr *bufio.Writer) error {
wr.WriteUint64(*id.id)
if trace {
outLogger.Printf("statement id: %d", *id.id)
}
return nil
}

View File

@ -1,52 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
// TableResult is the package internal representation of a table like output parameter of a stored procedure.
type TableResult struct {
id uint64
resultFieldSet *ResultFieldSet
fieldValues *FieldValues
attrs partAttributes
}
func newTableResult(s *Session, size int) *TableResult {
return &TableResult{
resultFieldSet: newResultFieldSet(size),
fieldValues: newFieldValues(),
}
}
// ID returns the resultset id.
func (r *TableResult) ID() uint64 {
return r.id
}
// FieldSet returns the field metadata of the table.
func (r *TableResult) FieldSet() *ResultFieldSet {
return r.resultFieldSet
}
// FieldValues returns the field values (fetched resultset part) of the table.
func (r *TableResult) FieldValues() *FieldValues {
return r.fieldValues
}
// Attrs returns the PartAttributes interface of the fetched resultset part.
func (r *TableResult) Attrs() PartAttributes {
return r.attrs
}

View File

@ -1,64 +0,0 @@
/*
Copyright 2017 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"time"
)
const gregorianDay = 2299161 // Start date of Gregorian Calendar as Julian Day Number
var gregorianDate = julianDayToTime(gregorianDay) // Start date of Gregorian Calendar (1582-10-15)
// timeToJulianDay returns the Julian Date Number of time's date components.
// The algorithm is taken from https://en.wikipedia.org/wiki/Julian_day.
func timeToJulianDay(t time.Time) int {
t = t.UTC()
month := int(t.Month())
a := (14 - month) / 12
y := t.Year() + 4800 - a
m := month + (12 * a) - 3
if t.Before(gregorianDate) { // Julian Calendar
return t.Day() + (153*m+2)/5 + 365*y + y/4 - 32083
}
// Gregorian Calendar
return t.Day() + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
}
// JulianDayToTime returns the correcponding UTC date for a Julian Day Number.
// The algorithm is taken from https://en.wikipedia.org/wiki/Julian_day.
func julianDayToTime(jd int) time.Time {
var f int
if jd < gregorianDay {
f = jd + 1401
} else {
f = jd + 1401 + (((4*jd+274277)/146097)*3)/4 - 38
}
e := 4*f + 3
g := (e % 1461) / 4
h := 5*g + 2
day := (h%153)/5 + 1
month := (h/153+2)%12 + 1
year := (e / 1461) - 4716 + (12+2-month)/12
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}

View File

@ -1,85 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
type topologyInformation struct {
mlo multiLineOptions
_numArg int
}
func newTopologyInformation() *topologyInformation {
return &topologyInformation{
mlo: multiLineOptions{},
}
}
func (o *topologyInformation) String() string {
mlo := make([]map[topologyOption]interface{}, len(o.mlo))
for i, po := range o.mlo {
typedPo := make(map[topologyOption]interface{})
for k, v := range po {
typedPo[topologyOption(k)] = v
}
mlo[i] = typedPo
}
return fmt.Sprintf("%s", mlo)
}
func (o *topologyInformation) kind() partKind {
return pkTopologyInformation
}
func (o *topologyInformation) size() int {
return o.mlo.size()
}
func (o *topologyInformation) numArg() int {
return len(o.mlo)
}
func (o *topologyInformation) setNumArg(numArg int) {
o._numArg = numArg
}
func (o *topologyInformation) read(rd *bufio.Reader) error {
o.mlo.read(rd, o._numArg)
if trace {
outLogger.Printf("topology options: %v", o)
}
return rd.GetError()
}
func (o *topologyInformation) write(wr *bufio.Writer) error {
for _, m := range o.mlo {
wr.WriteInt16(int16(len(m)))
o.mlo.write(wr)
}
if trace {
outLogger.Printf("topology options: %v", o)
}
return nil
}

View File

@ -1,36 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=topologyOption
type topologyOption int8
const (
toHostName topologyOption = 1
toHostPortnumber topologyOption = 2
toTenantName topologyOption = 3
toLoadfactor topologyOption = 4
toVolumeID topologyOption = 5
toIsMaster topologyOption = 6
toIsCurrentSession topologyOption = 7
toServiceType topologyOption = 8
toNetworkDomain topologyOption = 9
toIsStandby topologyOption = 10
toAllIPAddresses topologyOption = 11
toAllHostNames topologyOption = 12
)

View File

@ -1,17 +0,0 @@
// Code generated by "stringer -type=topologyOption"; DO NOT EDIT.
package protocol
import "strconv"
const _topologyOption_name = "toHostNametoHostPortnumbertoTenantNametoLoadfactortoVolumeIDtoIsMastertoIsCurrentSessiontoServiceTypetoNetworkDomaintoIsStandbytoAllIPAddressestoAllHostNames"
var _topologyOption_index = [...]uint8{0, 10, 26, 38, 50, 60, 70, 88, 101, 116, 127, 143, 157}
func (i topologyOption) String() string {
i -= 1
if i < 0 || i >= topologyOption(len(_topologyOption_index)-1) {
return "topologyOption(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _topologyOption_name[_topologyOption_index[i]:_topologyOption_index[i+1]]
}

View File

@ -1,60 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"fmt"
"github.com/SAP/go-hdb/internal/bufio"
)
type transactionFlags struct {
options plainOptions
_numArg int
}
func newTransactionFlags() *transactionFlags {
return &transactionFlags{
options: plainOptions{},
}
}
func (f *transactionFlags) String() string {
typedSc := make(map[transactionFlagType]interface{})
for k, v := range f.options {
typedSc[transactionFlagType(k)] = v
}
return fmt.Sprintf("%s", typedSc)
}
func (f *transactionFlags) kind() partKind {
return pkTransactionFlags
}
func (f *transactionFlags) setNumArg(numArg int) {
f._numArg = numArg
}
func (f *transactionFlags) read(rd *bufio.Reader) error {
f.options.read(rd, f._numArg)
if trace {
outLogger.Printf("transaction flags: %v", f)
}
return rd.GetError()
}

View File

@ -1,32 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
//go:generate stringer -type=transactionFlagType
//transaction flags
type transactionFlagType int8
const (
tfRolledback transactionFlagType = 0
tfCommited transactionFlagType = 1
tfNewIsolationLevel transactionFlagType = 2
tfDDLCommitmodeChanged transactionFlagType = 3
tfWriteTransactionStarted transactionFlagType = 4
tfNowriteTransactionStarted transactionFlagType = 5
tfSessionClosingTransactionError transactionFlagType = 6
)

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type=transactionFlagType"; DO NOT EDIT.
package protocol
import "strconv"
const _transactionFlagType_name = "tfRolledbacktfCommitedtfNewIsolationLeveltfDDLCommitmodeChangedtfWriteTransactionStartedtfNowriteTransactionStartedtfSessionClosingTransactionError"
var _transactionFlagType_index = [...]uint8{0, 12, 22, 41, 63, 88, 115, 147}
func (i transactionFlagType) String() string {
if i < 0 || i >= transactionFlagType(len(_transactionFlagType_index)-1) {
return "transactionFlagType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _transactionFlagType_name[_transactionFlagType_index[i]:_transactionFlagType_index[i+1]]
}

View File

@ -1,159 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"strings"
)
//go:generate stringer -type=TypeCode
// TypeCode identify the type of a field transferred to or from the database.
type TypeCode byte
// null value indicator is high bit
const (
tcNull TypeCode = 0
tcTinyint TypeCode = 1
tcSmallint TypeCode = 2
tcInteger TypeCode = 3
tcBigint TypeCode = 4
tcDecimal TypeCode = 5
tcReal TypeCode = 6
tcDouble TypeCode = 7
tcChar TypeCode = 8
tcVarchar TypeCode = 9
tcNchar TypeCode = 10
tcNvarchar TypeCode = 11
tcBinary TypeCode = 12
tcVarbinary TypeCode = 13
// deprecated with 3 (doku) - but table 'date' field uses it
tcDate TypeCode = 14
// deprecated with 3 (doku) - but table 'time' field uses it
tcTime TypeCode = 15
// deprecated with 3 (doku) - but table 'timestamp' field uses it
tcTimestamp TypeCode = 16
//tcTimetz TypeCode = 17 // reserved: do not use
//tcTimeltz TypeCode = 18 // reserved: do not use
//tcTimestamptz TypeCode = 19 // reserved: do not use
//tcTimestampltz TypeCode = 20 // reserved: do not use
//tcInvervalym TypeCode = 21 // reserved: do not use
//tcInvervalds TypeCode = 22 // reserved: do not use
//tcRowid TypeCode = 23 // reserved: do not use
//tcUrowid TypeCode = 24 // reserved: do not use
tcClob TypeCode = 25
tcNclob TypeCode = 26
tcBlob TypeCode = 27
tcBoolean TypeCode = 28
tcString TypeCode = 29
tcNstring TypeCode = 30
tcBlocator TypeCode = 31
tcNlocator TypeCode = 32
tcBstring TypeCode = 33
//tcDecimaldigitarray TypeCode = 34 // reserved: do not use
tcVarchar2 TypeCode = 35
tcVarchar3 TypeCode = 36
tcNvarchar3 TypeCode = 37
tcVarbinary3 TypeCode = 38
//tcVargroup TypeCode = 39 // reserved: do not use
//tcTinyintnotnull TypeCode = 40 // reserved: do not use
//tcSmallintnotnull TypeCode = 41 // reserved: do not use
//tcIntnotnull TypeCode = 42 // reserved: do not use
//tcBigintnotnull TypeCode = 43 // reserved: do not use
//tcArgument TypeCode = 44 // reserved: do not use
//tcTable TypeCode = 45 // reserved: do not use
//tcCursor TypeCode = 46 // reserved: do not use
tcSmalldecimal TypeCode = 47
//tcAbapitab TypeCode = 48 // not supported by GO hdb driver
//tcAbapstruct TypeCode = 49 // not supported by GO hdb driver
tcArray TypeCode = 50
tcText TypeCode = 51
tcShorttext TypeCode = 52
//tcFixedString TypeCode = 53 // reserved: do not use
//tcFixedpointdecimal TypeCode = 54 // reserved: do not use
tcAlphanum TypeCode = 55
//tcTlocator TypeCode = 56 // reserved: do not use
tcLongdate TypeCode = 61
tcSeconddate TypeCode = 62
tcDaydate TypeCode = 63
tcSecondtime TypeCode = 64
//tcCte TypeCode = 65 // reserved: do not use
//tcCstimesda TypeCode = 66 // reserved: do not use
//tcBlobdisk TypeCode = 71 // reserved: do not use
//tcClobdisk TypeCode = 72 // reserved: do not use
//tcNclobdisk TypeCode = 73 // reserved: do not use
//tcGeometry TypeCode = 74 // reserved: do not use
//tcPoint TypeCode = 75 // reserved: do not use
//tcFixed16 TypeCode = 76 // reserved: do not use
//tcBlobhybrid TypeCode = 77 // reserved: do not use
//tcClobhybrid TypeCode = 78 // reserved: do not use
//tcNclobhybrid TypeCode = 79 // reserved: do not use
//tcPointz TypeCode = 80 // reserved: do not use
)
func (k TypeCode) isLob() bool {
return k == tcClob || k == tcNclob || k == tcBlob
}
func (k TypeCode) isCharBased() bool {
return k == tcNvarchar || k == tcNstring || k == tcNclob
}
func (k TypeCode) isVariableLength() bool {
return k == tcChar || k == tcNchar || k == tcVarchar || k == tcNvarchar || k == tcBinary || k == tcVarbinary || k == tcShorttext || k == tcAlphanum
}
func (k TypeCode) isDecimalType() bool {
return k == tcSmalldecimal || k == tcDecimal
}
// DataType converts a type code into one of the supported data types by the driver.
func (k TypeCode) DataType() DataType {
switch k {
default:
return DtUnknown
case tcTinyint:
return DtTinyint
case tcSmallint:
return DtSmallint
case tcInteger:
return DtInteger
case tcBigint:
return DtBigint
case tcReal:
return DtReal
case tcDouble:
return DtDouble
case tcDate, tcTime, tcTimestamp, tcLongdate, tcSeconddate, tcDaydate, tcSecondtime:
return DtTime
case tcDecimal:
return DtDecimal
case tcChar, tcVarchar, tcString, tcNchar, tcNvarchar, tcNstring:
return DtString
case tcBinary, tcVarbinary:
return DtBytes
case tcBlob, tcClob, tcNclob:
return DtLob
}
}
// TypeName returns the database type name.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName
func (k TypeCode) TypeName() string {
return strings.ToUpper(k.String()[2:])
}

View File

@ -1,48 +0,0 @@
// Code generated by "stringer -type=TypeCode"; DO NOT EDIT.
package protocol
import "strconv"
const (
_TypeCode_name_0 = "tcNulltcTinyinttcSmallinttcIntegertcBiginttcDecimaltcRealtcDoubletcChartcVarchartcNchartcNvarchartcBinarytcVarbinarytcDatetcTimetcTimestamp"
_TypeCode_name_1 = "tcClobtcNclobtcBlobtcBooleantcStringtcNstringtcBlocatortcNlocatortcBstring"
_TypeCode_name_2 = "tcVarchar2tcVarchar3tcNvarchar3tcVarbinary3"
_TypeCode_name_3 = "tcSmalldecimal"
_TypeCode_name_4 = "tcArraytcTexttcShorttext"
_TypeCode_name_5 = "tcAlphanum"
_TypeCode_name_6 = "tcLongdatetcSeconddatetcDaydatetcSecondtime"
)
var (
_TypeCode_index_0 = [...]uint8{0, 6, 15, 25, 34, 42, 51, 57, 65, 71, 80, 87, 97, 105, 116, 122, 128, 139}
_TypeCode_index_1 = [...]uint8{0, 6, 13, 19, 28, 36, 45, 55, 65, 74}
_TypeCode_index_2 = [...]uint8{0, 10, 20, 31, 43}
_TypeCode_index_4 = [...]uint8{0, 7, 13, 24}
_TypeCode_index_6 = [...]uint8{0, 10, 22, 31, 43}
)
func (i TypeCode) String() string {
switch {
case 0 <= i && i <= 16:
return _TypeCode_name_0[_TypeCode_index_0[i]:_TypeCode_index_0[i+1]]
case 25 <= i && i <= 33:
i -= 25
return _TypeCode_name_1[_TypeCode_index_1[i]:_TypeCode_index_1[i+1]]
case 35 <= i && i <= 38:
i -= 35
return _TypeCode_name_2[_TypeCode_index_2[i]:_TypeCode_index_2[i+1]]
case i == 47:
return _TypeCode_name_3
case 50 <= i && i <= 52:
i -= 50
return _TypeCode_name_4[_TypeCode_index_4[i]:_TypeCode_index_4[i+1]]
case i == 55:
return _TypeCode_name_5
case 61 <= i && i <= 64:
i -= 61
return _TypeCode_name_6[_TypeCode_index_6[i]:_TypeCode_index_6[i+1]]
default:
return "TypeCode(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@ -1,240 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package cesu8 implements functions and constants to support text encoded in CESU-8.
// It implements functions comparable to the unicode/utf8 package for UTF-8 de- and encoding.
package cesu8
import (
"unicode/utf16"
"unicode/utf8"
)
const (
// CESUMax is the maximum amount of bytes used by an CESU-8 codepoint encoding.
CESUMax = 6
)
// Size returns the amount of bytes needed to encode an UTF-8 byte slice to CESU-8.
func Size(p []byte) int {
n := 0
for i := 0; i < len(p); {
r, size, _ := decodeRune(p[i:])
i += size
n += RuneLen(r)
}
return n
}
// StringSize is like Size with a string as parameter.
func StringSize(s string) int {
n := 0
for _, r := range s {
n += RuneLen(r)
}
return n
}
// EncodeRune writes into p (which must be large enough) the CESU-8 encoding of the rune. It returns the number of bytes written.
func EncodeRune(p []byte, r rune) int {
if r <= rune3Max {
return encodeRune(p, r)
}
high, low := utf16.EncodeRune(r)
n := encodeRune(p, high)
n += encodeRune(p[n:], low)
return n
}
// FullRune reports whether the bytes in p begin with a full CESU-8 encoding of a rune.
func FullRune(p []byte) bool {
high, n, short := decodeRune(p)
if short {
return false
}
if !utf16.IsSurrogate(high) {
return true
}
_, _, short = decodeRune(p[n:])
return !short
}
// DecodeRune unpacks the first CESU-8 encoding in p and returns the rune and its width in bytes.
func DecodeRune(p []byte) (rune, int) {
high, n1, _ := decodeRune(p)
if !utf16.IsSurrogate(high) {
return high, n1
}
low, n2, _ := decodeRune(p[n1:])
if low == utf8.RuneError {
return low, n1 + n2
}
return utf16.DecodeRune(high, low), n1 + n2
}
// RuneLen returns the number of bytes required to encode the rune.
func RuneLen(r rune) int {
switch {
case r < 0:
return -1
case r <= rune1Max:
return 1
case r <= rune2Max:
return 2
case r <= rune3Max:
return 3
case r <= utf8.MaxRune:
return CESUMax
}
return -1
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Copied from unicode utf8
// - allow utf8 encoding of utf16 surrogate values
// - see (*) for code changes
// Code points in the surrogate range are not valid for UTF-8.
const (
surrogateMin = 0xD800
surrogateMax = 0xDFFF
)
const (
t1 = 0x00 // 0000 0000
tx = 0x80 // 1000 0000
t2 = 0xC0 // 1100 0000
t3 = 0xE0 // 1110 0000
t4 = 0xF0 // 1111 0000
t5 = 0xF8 // 1111 1000
maskx = 0x3F // 0011 1111
mask2 = 0x1F // 0001 1111
mask3 = 0x0F // 0000 1111
mask4 = 0x07 // 0000 0111
rune1Max = 1<<7 - 1
rune2Max = 1<<11 - 1
rune3Max = 1<<16 - 1
)
func encodeRune(p []byte, r rune) int {
// Negative values are erroneous. Making it unsigned addresses the problem.
switch i := uint32(r); {
case i <= rune1Max:
p[0] = byte(r)
return 1
case i <= rune2Max:
p[0] = t2 | byte(r>>6)
p[1] = tx | byte(r)&maskx
return 2
//case i > MaxRune, surrogateMin <= i && i <= surrogateMax: // replaced (*)
case i > utf8.MaxRune: // (*)
r = utf8.RuneError
fallthrough
case i <= rune3Max:
p[0] = t3 | byte(r>>12)
p[1] = tx | byte(r>>6)&maskx
p[2] = tx | byte(r)&maskx
return 3
default:
p[0] = t4 | byte(r>>18)
p[1] = tx | byte(r>>12)&maskx
p[2] = tx | byte(r>>6)&maskx
p[3] = tx | byte(r)&maskx
return 4
}
}
func decodeRune(p []byte) (r rune, size int, short bool) {
n := len(p)
if n < 1 {
return utf8.RuneError, 0, true
}
c0 := p[0]
// 1-byte, 7-bit sequence?
if c0 < tx {
return rune(c0), 1, false
}
// unexpected continuation byte?
if c0 < t2 {
return utf8.RuneError, 1, false
}
// need first continuation byte
if n < 2 {
return utf8.RuneError, 1, true
}
c1 := p[1]
if c1 < tx || t2 <= c1 {
return utf8.RuneError, 1, false
}
// 2-byte, 11-bit sequence?
if c0 < t3 {
r = rune(c0&mask2)<<6 | rune(c1&maskx)
if r <= rune1Max {
return utf8.RuneError, 1, false
}
return r, 2, false
}
// need second continuation byte
if n < 3 {
return utf8.RuneError, 1, true
}
c2 := p[2]
if c2 < tx || t2 <= c2 {
return utf8.RuneError, 1, false
}
// 3-byte, 16-bit sequence?
if c0 < t4 {
r = rune(c0&mask3)<<12 | rune(c1&maskx)<<6 | rune(c2&maskx)
if r <= rune2Max {
return utf8.RuneError, 1, false
}
// do not throw error on surrogates // (*)
//if surrogateMin <= r && r <= surrogateMax {
// return RuneError, 1, false
//}
return r, 3, false
}
// need third continuation byte
if n < 4 {
return utf8.RuneError, 1, true
}
c3 := p[3]
if c3 < tx || t2 <= c3 {
return utf8.RuneError, 1, false
}
// 4-byte, 21-bit sequence?
if c0 < t5 {
r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx)
if r <= rune3Max || utf8.MaxRune < r {
return utf8.RuneError, 1, false
}
return r, 4, false
}
// error
return utf8.RuneError, 1, false
}

View File

@ -1,111 +0,0 @@
/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package unicode implements UTF-8 to CESU-8 and vice versa transformations.
package unicode
import (
"errors"
"unicode/utf8"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
"golang.org/x/text/transform"
)
var (
// Utf8ToCesu8Transformer implements the golang.org/x/text/transform/Transformer interface for UTF-8 to CESU-8 transformation.
Utf8ToCesu8Transformer = new(utf8ToCesu8Transformer)
// Cesu8ToUtf8Transformer implements the golang.org/x/text/transform/Transformer interface for CESU-8 to UTF-8 transformation.
Cesu8ToUtf8Transformer = new(cesu8ToUtf8Transformer)
// ErrInvalidUtf8 means that a transformer detected invalid UTF-8 data.
ErrInvalidUtf8 = errors.New("Invalid UTF-8")
// ErrInvalidCesu8 means that a transformer detected invalid CESU-8 data.
ErrInvalidCesu8 = errors.New("Invalid CESU-8")
)
type utf8ToCesu8Transformer struct{ transform.NopResetter }
func (t *utf8ToCesu8Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
i, j := 0, 0
for i < len(src) {
if src[i] < utf8.RuneSelf {
if j < len(dst) {
dst[j] = src[i]
i++
j++
} else {
return j, i, transform.ErrShortDst
}
} else {
if !utf8.FullRune(src[i:]) {
return j, i, transform.ErrShortSrc
}
r, n := utf8.DecodeRune(src[i:])
if r == utf8.RuneError {
return j, i, ErrInvalidUtf8
}
m := cesu8.RuneLen(r)
if m == -1 {
panic("internal UTF-8 to CESU-8 transformation error")
}
if j+m <= len(dst) {
cesu8.EncodeRune(dst[j:], r)
i += n
j += m
} else {
return j, i, transform.ErrShortDst
}
}
}
return j, i, nil
}
type cesu8ToUtf8Transformer struct{ transform.NopResetter }
func (t *cesu8ToUtf8Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
i, j := 0, 0
for i < len(src) {
if src[i] < utf8.RuneSelf {
if j < len(dst) {
dst[j] = src[i]
i++
j++
} else {
return j, i, transform.ErrShortDst
}
} else {
if !cesu8.FullRune(src[i:]) {
return j, i, transform.ErrShortSrc
}
r, n := cesu8.DecodeRune(src[i:])
if r == utf8.RuneError {
return j, i, ErrInvalidCesu8
}
m := utf8.RuneLen(r)
if m == -1 {
panic("internal CESU-8 to UTF-8 transformation error")
}
if j+m <= len(dst) {
utf8.EncodeRune(dst[j:], r)
i += n
j += m
} else {
return j, i, transform.ErrShortDst
}
}
}
return j, i, nil
}

View File

@ -1,29 +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
*.test
*.prof
*.out
*.tmp
tags

View File

@ -1,14 +0,0 @@
language: go
go:
- 1.6
- 1.7
- tip
sudo: false
install:
- go get -u github.com/golang/lint/golint
script:
- ./_test.sh

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Sermo Digital LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,40 +0,0 @@
JOSE
============
[![Build Status](https://travis-ci.org/SermoDigital/jose.svg?branch=master)](https://travis-ci.org/SermoDigital/jose)
[![GoDoc](https://godoc.org/github.com/SermoDigital/jose?status.svg)](https://godoc.org/github.com/SermoDigital/jose)
JOSE is a comprehensive set of JWT, JWS, and JWE libraries.
## Why
The only other JWS/JWE/JWT implementations are specific to JWT, and none
were particularly pleasant to work with.
These libraries should provide an easy, straightforward way to securely
create, parse, and validate JWS, JWE, and JWTs.
## Notes:
JWE is currently unimplemented.
## Version 0.9:
## Documentation
The docs can be found at [godoc.org] [docs], as usual.
A gopkg.in mirror can be found at https://gopkg.in/jose.v1, thanks to
@zia-newversion. (For context, see issue #30.)
### [JWS RFC][jws]
### [JWE RFC][jwe]
### [JWT RFC][jwt]
## License
[MIT] [license].
[docs]: https://godoc.org/github.com/SermoDigital/jose
[license]: https://github.com/SermoDigital/jose/blob/master/LICENSE.md
[jws]: https://tools.ietf.org/html/rfc7515
[jwe]: https://tools.ietf.org/html/rfc7516
[jwt]: https://tools.ietf.org/html/rfc7519

View File

@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
go build ./...
go test ./...
golint ./...
go vet ./...

View File

@ -1,44 +0,0 @@
package jose
import "encoding/base64"
// Encoder is satisfied if the type can marshal itself into a valid
// structure for a JWS.
type Encoder interface {
// Base64 implies T -> JSON -> RawURLEncodingBase64
Base64() ([]byte, error)
}
// Base64Decode decodes a base64-encoded byte slice.
func Base64Decode(b []byte) ([]byte, error) {
buf := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
n, err := base64.RawURLEncoding.Decode(buf, b)
return buf[:n], err
}
// Base64Encode encodes a byte slice.
func Base64Encode(b []byte) []byte {
buf := make([]byte, base64.RawURLEncoding.EncodedLen(len(b)))
base64.RawURLEncoding.Encode(buf, b)
return buf
}
// EncodeEscape base64-encodes a byte slice but escapes it for JSON.
// It'll return the format: `"base64"`
func EncodeEscape(b []byte) []byte {
buf := make([]byte, base64.RawURLEncoding.EncodedLen(len(b))+2)
buf[0] = '"'
base64.RawURLEncoding.Encode(buf[1:], b)
buf[len(buf)-1] = '"'
return buf
}
// DecodeEscaped decodes a base64-encoded byte slice straight from a JSON
// structure. It assumes it's in the format: `"base64"`, but can handle
// cases where it's not.
func DecodeEscaped(b []byte) ([]byte, error) {
if len(b) > 1 && b[0] == '"' && b[len(b)-1] == '"' {
b = b[1 : len(b)-1]
}
return Base64Decode(b)
}

View File

@ -1,4 +0,0 @@
// Package crypto implements "SigningMethods" and "EncryptionMethods";
// that is, ways to sign and encrypt JWS and JWEs, respectively, as well
// as JWTs.
package crypto

View File

@ -1,117 +0,0 @@
package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"encoding/asn1"
"encoding/json"
"errors"
"math/big"
)
// ErrECDSAVerification is missing from crypto/ecdsa compared to crypto/rsa
var ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
// SigningMethodECDSA implements the ECDSA family of signing methods signing
// methods
type SigningMethodECDSA struct {
Name string
Hash crypto.Hash
_ struct{}
}
// ECPoint is a marshalling structure for the EC points R and S.
type ECPoint struct {
R *big.Int
S *big.Int
}
// Specific instances of EC SigningMethods.
var (
// SigningMethodES256 implements ES256.
SigningMethodES256 = &SigningMethodECDSA{
Name: "ES256",
Hash: crypto.SHA256,
}
// SigningMethodES384 implements ES384.
SigningMethodES384 = &SigningMethodECDSA{
Name: "ES384",
Hash: crypto.SHA384,
}
// SigningMethodES512 implements ES512.
SigningMethodES512 = &SigningMethodECDSA{
Name: "ES512",
Hash: crypto.SHA512,
}
)
// Alg returns the name of the SigningMethodECDSA instance.
func (m *SigningMethodECDSA) Alg() string { return m.Name }
// Verify implements the Verify method from SigningMethod.
// For this verify method, key must be an *ecdsa.PublicKey.
func (m *SigningMethodECDSA) Verify(raw []byte, signature Signature, key interface{}) error {
ecdsaKey, ok := key.(*ecdsa.PublicKey)
if !ok {
return ErrInvalidKey
}
// Unmarshal asn1 ECPoint
var ecpoint ECPoint
if _, err := asn1.Unmarshal(signature, &ecpoint); err != nil {
return err
}
// Verify the signature
if !ecdsa.Verify(ecdsaKey, m.sum(raw), ecpoint.R, ecpoint.S) {
return ErrECDSAVerification
}
return nil
}
// Sign implements the Sign method from SigningMethod.
// For this signing method, key must be an *ecdsa.PrivateKey.
func (m *SigningMethodECDSA) Sign(data []byte, key interface{}) (Signature, error) {
ecdsaKey, ok := key.(*ecdsa.PrivateKey)
if !ok {
return nil, ErrInvalidKey
}
r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, m.sum(data))
if err != nil {
return nil, err
}
signature, err := asn1.Marshal(ECPoint{R: r, S: s})
if err != nil {
return nil, err
}
return Signature(signature), nil
}
func (m *SigningMethodECDSA) sum(b []byte) []byte {
h := m.Hash.New()
h.Write(b)
return h.Sum(nil)
}
// Hasher implements the Hasher method from SigningMethod.
func (m *SigningMethodECDSA) Hasher() crypto.Hash {
return m.Hash
}
// MarshalJSON is in case somebody decides to place SigningMethodECDSA
// inside the Header, presumably because they (wrongly) decided it was a good
// idea to use the SigningMethod itself instead of the SigningMethod's Alg
// method. In order to keep things sane, marshalling this will simply
// return the JSON-compatible representation of m.Alg().
func (m *SigningMethodECDSA) MarshalJSON() ([]byte, error) {
return []byte(`"` + m.Alg() + `"`), nil
}
var _ json.Marshaler = (*SigningMethodECDSA)(nil)

View File

@ -1,48 +0,0 @@
package crypto
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"errors"
)
// ECDSA parsing errors.
var (
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
)
// ParseECPrivateKeyFromPEM will parse a PEM encoded EC Private
// Key Structure.
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
block, _ := pem.Decode(key)
if block == nil {
return nil, ErrKeyMustBePEMEncoded
}
return x509.ParseECPrivateKey(block.Bytes)
}
// ParseECPublicKeyFromPEM will parse a PEM encoded PKCS1 or PKCS8 public key
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
block, _ := pem.Decode(key)
if block == nil {
return nil, ErrKeyMustBePEMEncoded
}
parsedKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
parsedKey = cert.PublicKey
}
pkey, ok := parsedKey.(*ecdsa.PublicKey)
if !ok {
return nil, ErrNotECPublicKey
}
return pkey, nil
}

View File

@ -1,9 +0,0 @@
package crypto
import "errors"
var (
// ErrInvalidKey means the key argument passed to SigningMethod.Verify
// was not the correct type.
ErrInvalidKey = errors.New("key is invalid")
)

View File

@ -1,81 +0,0 @@
package crypto
import (
"crypto"
"crypto/hmac"
"encoding/json"
"errors"
)
// SigningMethodHMAC implements the HMAC-SHA family of SigningMethods.
type SigningMethodHMAC struct {
Name string
Hash crypto.Hash
_ struct{}
}
// Specific instances of HMAC-SHA SigningMethods.
var (
// SigningMethodHS256 implements HS256.
SigningMethodHS256 = &SigningMethodHMAC{
Name: "HS256",
Hash: crypto.SHA256,
}
// SigningMethodHS384 implements HS384.
SigningMethodHS384 = &SigningMethodHMAC{
Name: "HS384",
Hash: crypto.SHA384,
}
// SigningMethodHS512 implements HS512.
SigningMethodHS512 = &SigningMethodHMAC{
Name: "HS512",
Hash: crypto.SHA512,
}
// ErrSignatureInvalid is returned when the provided signature is found
// to be invalid.
ErrSignatureInvalid = errors.New("signature is invalid")
)
// Alg implements the SigningMethod interface.
func (m *SigningMethodHMAC) Alg() string { return m.Name }
// Verify implements the Verify method from SigningMethod.
// For this signing method, must be a []byte.
func (m *SigningMethodHMAC) Verify(raw []byte, signature Signature, key interface{}) error {
keyBytes, ok := key.([]byte)
if !ok {
return ErrInvalidKey
}
hasher := hmac.New(m.Hash.New, keyBytes)
hasher.Write(raw)
if hmac.Equal(signature, hasher.Sum(nil)) {
return nil
}
return ErrSignatureInvalid
}
// Sign implements the Sign method from SigningMethod for this signing method.
// Key must be a []byte.
func (m *SigningMethodHMAC) Sign(data []byte, key interface{}) (Signature, error) {
keyBytes, ok := key.([]byte)
if !ok {
return nil, ErrInvalidKey
}
hasher := hmac.New(m.Hash.New, keyBytes)
hasher.Write(data)
return Signature(hasher.Sum(nil)), nil
}
// Hasher implements the SigningMethod interface.
func (m *SigningMethodHMAC) Hasher() crypto.Hash { return m.Hash }
// MarshalJSON implements json.Marshaler.
// See SigningMethodECDSA.MarshalJSON() for information.
func (m *SigningMethodHMAC) MarshalJSON() ([]byte, error) {
return []byte(`"` + m.Alg() + `"`), nil
}
var _ json.Marshaler = (*SigningMethodHMAC)(nil)

View File

@ -1,72 +0,0 @@
package crypto
import (
"crypto"
"encoding/json"
"hash"
"io"
)
func init() {
crypto.RegisterHash(crypto.Hash(0), h)
}
// h is passed to crypto.RegisterHash.
func h() hash.Hash {
return &f{Writer: nil}
}
type f struct{ io.Writer }
// Sum helps implement the hash.Hash interface.
func (_ *f) Sum(b []byte) []byte { return nil }
// Reset helps implement the hash.Hash interface.
func (_ *f) Reset() {}
// Size helps implement the hash.Hash interface.
func (_ *f) Size() int { return -1 }
// BlockSize helps implement the hash.Hash interface.
func (_ *f) BlockSize() int { return -1 }
// Unsecured is the default "none" algorithm.
var Unsecured = &SigningMethodNone{
Name: "none",
Hash: crypto.Hash(0),
}
// SigningMethodNone is the default "none" algorithm.
type SigningMethodNone struct {
Name string
Hash crypto.Hash
_ struct{}
}
// Verify helps implement the SigningMethod interface.
func (_ *SigningMethodNone) Verify(_ []byte, _ Signature, _ interface{}) error {
return nil
}
// Sign helps implement the SigningMethod interface.
func (_ *SigningMethodNone) Sign(_ []byte, _ interface{}) (Signature, error) {
return nil, nil
}
// Alg helps implement the SigningMethod interface.
func (m *SigningMethodNone) Alg() string {
return m.Name
}
// Hasher helps implement the SigningMethod interface.
func (m *SigningMethodNone) Hasher() crypto.Hash {
return m.Hash
}
// MarshalJSON implements json.Marshaler.
// See SigningMethodECDSA.MarshalJSON() for information.
func (m *SigningMethodNone) MarshalJSON() ([]byte, error) {
return []byte(`"` + m.Alg() + `"`), nil
}
var _ json.Marshaler = (*SigningMethodNone)(nil)

View File

@ -1,80 +0,0 @@
package crypto
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"encoding/json"
)
// SigningMethodRSA implements the RSA family of SigningMethods.
type SigningMethodRSA struct {
Name string
Hash crypto.Hash
_ struct{}
}
// Specific instances of RSA SigningMethods.
var (
// SigningMethodRS256 implements RS256.
SigningMethodRS256 = &SigningMethodRSA{
Name: "RS256",
Hash: crypto.SHA256,
}
// SigningMethodRS384 implements RS384.
SigningMethodRS384 = &SigningMethodRSA{
Name: "RS384",
Hash: crypto.SHA384,
}
// SigningMethodRS512 implements RS512.
SigningMethodRS512 = &SigningMethodRSA{
Name: "RS512",
Hash: crypto.SHA512,
}
)
// Alg implements the SigningMethod interface.
func (m *SigningMethodRSA) Alg() string { return m.Name }
// Verify implements the Verify method from SigningMethod.
// For this signing method, must be an *rsa.PublicKey.
func (m *SigningMethodRSA) Verify(raw []byte, sig Signature, key interface{}) error {
rsaKey, ok := key.(*rsa.PublicKey)
if !ok {
return ErrInvalidKey
}
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, m.sum(raw), sig)
}
// Sign implements the Sign method from SigningMethod.
// For this signing method, must be an *rsa.PrivateKey structure.
func (m *SigningMethodRSA) Sign(data []byte, key interface{}) (Signature, error) {
rsaKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, ErrInvalidKey
}
sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, m.sum(data))
if err != nil {
return nil, err
}
return Signature(sigBytes), nil
}
func (m *SigningMethodRSA) sum(b []byte) []byte {
h := m.Hash.New()
h.Write(b)
return h.Sum(nil)
}
// Hasher implements the SigningMethod interface.
func (m *SigningMethodRSA) Hasher() crypto.Hash { return m.Hash }
// MarshalJSON implements json.Marshaler.
// See SigningMethodECDSA.MarshalJSON() for information.
func (m *SigningMethodRSA) MarshalJSON() ([]byte, error) {
return []byte(`"` + m.Alg() + `"`), nil
}
var _ json.Marshaler = (*SigningMethodRSA)(nil)

View File

@ -1,96 +0,0 @@
// +build go1.4
package crypto
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"encoding/json"
)
// SigningMethodRSAPSS implements the RSAPSS family of SigningMethods.
type SigningMethodRSAPSS struct {
*SigningMethodRSA
Options *rsa.PSSOptions
}
// Specific instances for RS/PS SigningMethods.
var (
// SigningMethodPS256 implements PS256.
SigningMethodPS256 = &SigningMethodRSAPSS{
&SigningMethodRSA{
Name: "PS256",
Hash: crypto.SHA256,
},
&rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA256,
},
}
// SigningMethodPS384 implements PS384.
SigningMethodPS384 = &SigningMethodRSAPSS{
&SigningMethodRSA{
Name: "PS384",
Hash: crypto.SHA384,
},
&rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA384,
},
}
// SigningMethodPS512 implements PS512.
SigningMethodPS512 = &SigningMethodRSAPSS{
&SigningMethodRSA{
Name: "PS512",
Hash: crypto.SHA512,
},
&rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto,
Hash: crypto.SHA512,
},
}
)
// Verify implements the Verify method from SigningMethod.
// For this verify method, key must be an *rsa.PublicKey.
func (m *SigningMethodRSAPSS) Verify(raw []byte, signature Signature, key interface{}) error {
rsaKey, ok := key.(*rsa.PublicKey)
if !ok {
return ErrInvalidKey
}
return rsa.VerifyPSS(rsaKey, m.Hash, m.sum(raw), signature, m.Options)
}
// Sign implements the Sign method from SigningMethod.
// For this signing method, key must be an *rsa.PrivateKey.
func (m *SigningMethodRSAPSS) Sign(raw []byte, key interface{}) (Signature, error) {
rsaKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, ErrInvalidKey
}
sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, m.sum(raw), m.Options)
if err != nil {
return nil, err
}
return Signature(sigBytes), nil
}
func (m *SigningMethodRSAPSS) sum(b []byte) []byte {
h := m.Hash.New()
h.Write(b)
return h.Sum(nil)
}
// Hasher implements the Hasher method from SigningMethod.
func (m *SigningMethodRSAPSS) Hasher() crypto.Hash { return m.Hash }
// MarshalJSON implements json.Marshaler.
// See SigningMethodECDSA.MarshalJSON() for information.
func (m *SigningMethodRSAPSS) MarshalJSON() ([]byte, error) {
return []byte(`"` + m.Alg() + `"`), nil
}
var _ json.Marshaler = (*SigningMethodRSAPSS)(nil)

View File

@ -1,70 +0,0 @@
package crypto
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
)
// Errors specific to rsa_utils.
var (
ErrKeyMustBePEMEncoded = errors.New("invalid key: Key must be PEM encoded PKCS1 or PKCS8 private key")
ErrNotRSAPrivateKey = errors.New("key is not a valid RSA private key")
ErrNotRSAPublicKey = errors.New("key is not a valid RSA public key")
)
// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key.
func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
var err error
// Parse PEM block
var block *pem.Block
if block, _ = pem.Decode(key); block == nil {
return nil, ErrKeyMustBePEMEncoded
}
var parsedKey interface{}
if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
return nil, err
}
}
var pkey *rsa.PrivateKey
var ok bool
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
return nil, ErrNotRSAPrivateKey
}
return pkey, nil
}
// ParseRSAPublicKeyFromPEM parses PEM encoded PKCS1 or PKCS8 public key.
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
var err error
// Parse PEM block
var block *pem.Block
if block, _ = pem.Decode(key); block == nil {
return nil, ErrKeyMustBePEMEncoded
}
// Parse the key
var parsedKey interface{}
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
parsedKey = cert.PublicKey
} else {
return nil, err
}
}
var pkey *rsa.PublicKey
var ok bool
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
return nil, ErrNotRSAPublicKey
}
return pkey, nil
}

View File

@ -1,36 +0,0 @@
package crypto
import (
"encoding/json"
"github.com/SermoDigital/jose"
)
// Signature is a JWS signature.
type Signature []byte
// MarshalJSON implements json.Marshaler for a signature.
func (s Signature) MarshalJSON() ([]byte, error) {
return jose.EncodeEscape(s), nil
}
// Base64 helps implements jose.Encoder for Signature.
func (s Signature) Base64() ([]byte, error) {
return jose.Base64Encode(s), nil
}
// UnmarshalJSON implements json.Unmarshaler for signature.
func (s *Signature) UnmarshalJSON(b []byte) error {
dec, err := jose.DecodeEscaped(b)
if err != nil {
return err
}
*s = Signature(dec)
return nil
}
var (
_ json.Marshaler = (Signature)(nil)
_ json.Unmarshaler = (*Signature)(nil)
_ jose.Encoder = (Signature)(nil)
)

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