Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

add go-mssqldb tests #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,13 @@ this will result connecting to database user/password@sid
The corner case where the sid is a full DSN (with port and everything) is not
fully managed: only port 1521 on localhost is checked for accepting connections.
If you really need the full DSN support, please mail me!

****************************************************************************
For MSSQL:
****************************************************************************
osql -Q "CREATE DATABASE gotestsql"

To set set your MSSQL user / password run:

$ export GOSQLTEST_MSSQL_USER=user
$ export GOSQLTEST_MSSQL_PASS=password
27 changes: 27 additions & 0 deletions src/github.com/denisenkom/go-mssqldb/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72 changes: 72 additions & 0 deletions src/github.com/denisenkom/go-mssqldb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# A pure Go MSSQL driver for Go's database/sql package

## Install

go get github.com/denisenkom/go-mssqldb

## Tests

`go test` is used for testing. A running instance of MSSQL server is required.
Environment variables are used to pass login information.

Example:

env HOST=localhost SQLUSER=sa SQLPASSWORD=sa DATABASE=test go test

## Connection Parameters

* "server" - host or host\instance
* "port" - used only when there is no instance in server (default 1433)
* "user id"
* "password"
* "database"
* "connection timeout" - in seconds (default is 30)
* "keepAlive" - in seconds; 0 to disable (default is 0)
* "log" - logging flags (default 0/no logging, 63 for full logging)
* 1 log errors
* 2 log messages
* 4 log rows affected
* 8 trace sql statements
* 16 log statement parameters
* 32 log transaction begin/end
* "encrypt"
* disable - Data send between client and server is not encrypted.
* false - Data sent between client and server is not encrypted beyond the login packet. (Default)
* true - Data sent between client and server is encrypted.
* "TrustServerCertificate"
* false - Server certificate is checked. Default is false if encypt is specified.
* true - Server certificate is not checked. Default is true if encrypt is not specified. If trust server certificate is true, driver accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.
* "certificate" - The file that contains the public key certificate of the CA that signed the SQL Server certificate. The specified certificate overrides the go platform specific CA certificates.
* "hostNameInCertificate" - Specifies the Common Name (CN) in the server certificate. Default value is the server host.

Example:

```go
db, err := sql.Open("mssql", "server=localhost;user id=sa")
```

## Statement Parameters

In the SQL statement text, literals may be replaced by a parameter that matches one of the following:

* ?
* ?nnn
* :nnn
* $nnn

where nnn represents an integer.

## Features

* Can be used with SQL Server 2005 or newer
* Can be used on all go supported platforms (e.g. Linux, Mac OS X and Windows)
* Supports new date/time types: date, time, datetime2, datetimeoffset
* Supports string parameters longer that 8000 characters
* Supports encryption using SSL/TLS

## Known Issues

* SQL Server 2008 and 2008 R2 engine cannot handle login records when SSL encryption is not disabled.
To fix SQL Server 2008 R2 issue, install SQL Server 2008 R2 Service Pack 2.
To fix SQL Server 2008 issue, install Microsoft SQL Server 2008 Service Pack 3 and Cumulative update package 3 for SQL Server 2008 SP3.
More information: http://support.microsoft.com/kb/2653857
212 changes: 212 additions & 0 deletions src/github.com/denisenkom/go-mssqldb/buf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package mssql

import (
"encoding/binary"
"io"
)

type header struct {
PacketType uint8
Status uint8
Size uint16
Spid uint16
PacketNo uint8
Pad uint8
}

type tdsBuffer struct {
buf []byte
pos uint16
transport io.ReadWriteCloser
size uint16
final bool
packet_type uint8
afterFirst func()
}

func newTdsBuffer(bufsize int, transport io.ReadWriteCloser) *tdsBuffer {
buf := make([]byte, bufsize)
w := new(tdsBuffer)
w.buf = buf
w.pos = 8
w.transport = transport
w.size = 0
return w
}

func (w *tdsBuffer) flush() (err error) {
binary.BigEndian.PutUint16(w.buf[2:], w.pos)
if _, err = w.transport.Write(w.buf[:w.pos]); err != nil {
return err
}
if w.afterFirst != nil {
w.afterFirst()
w.afterFirst = nil
}
w.pos = 8
w.buf[6] += 1
return nil
}

func (w *tdsBuffer) Write(p []byte) (nn int, err error) {
total := 0
for {
copied := copy(w.buf[w.pos:], p)
w.pos += uint16(copied)
total += copied
if copied == len(p) {
break
}
if err = w.flush(); err != nil {
return total, err
}
p = p[copied:]
}
return total, nil
}

func (w *tdsBuffer) WriteByte(b byte) error {
if int(w.pos) == len(w.buf) {
if err := w.flush(); err != nil {
return err
}
}
w.buf[w.pos] = b
w.pos += 1
return nil
}

func (w *tdsBuffer) BeginPacket(packet_type byte) {
w.buf[0] = packet_type
w.buf[1] = 0 // packet is incomplete
w.buf[4] = 0 // spid
w.buf[5] = 0
w.buf[6] = 1 // packet id
w.buf[7] = 0 // window
w.pos = 8
}

func (w *tdsBuffer) FinishPacket() (err error) {
w.buf[1] = 1 // packet is complete
binary.BigEndian.PutUint16(w.buf[2:], w.pos)
_, err = w.transport.Write(w.buf[:w.pos])
if w.afterFirst != nil {
w.afterFirst()
w.afterFirst = nil
}
return err
}

func (r *tdsBuffer) readNextPacket() error {
header := header{}
var err error
err = binary.Read(r.transport, binary.BigEndian, &header)
if err != nil {
return err
}
offset := uint16(binary.Size(header))
_, err = io.ReadFull(r.transport, r.buf[offset:header.Size])
if err != nil {
return err
}
r.pos = offset
r.size = header.Size
r.final = header.Status != 0
r.packet_type = header.PacketType
return nil
}

func (r *tdsBuffer) BeginRead() (uint8, error) {
err := r.readNextPacket()
if err != nil {
return 0, err
}
return r.packet_type, nil
}

func (r *tdsBuffer) ReadByte() (res byte, err error) {
if r.pos == r.size {
if r.final {
return 0, io.EOF
}
err = r.readNextPacket()
if err != nil {
return 0, err
}
}
res = r.buf[r.pos]
r.pos++
return res, nil
}

func (r *tdsBuffer) byte() byte {
b, err := r.ReadByte()
if err != nil {
badStreamPanic(err)
}
return b
}

func (r *tdsBuffer) ReadFull(buf []byte) {
_, err := io.ReadFull(r, buf[:])
if err != nil {
badStreamPanic(err)
}
}

func (r *tdsBuffer) uint64() uint64 {
var buf [8]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint64(buf[:])
}

func (r *tdsBuffer) int32() int32 {
return int32(r.uint32())
}

func (r *tdsBuffer) uint32() uint32 {
var buf [4]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint32(buf[:])
}

func (r *tdsBuffer) uint16() uint16 {
var buf [2]byte
r.ReadFull(buf[:])
return binary.LittleEndian.Uint16(buf[:])
}

func (r *tdsBuffer) BVarChar() string {
l := int(r.byte())
return r.readUcs2(l)
}

func (r *tdsBuffer) UsVarChar() string {
l := int(r.uint16())
return r.readUcs2(l)
}

func (r *tdsBuffer) readUcs2(numchars int) string {
b := make([]byte, numchars*2)
r.ReadFull(b)
res, err := ucs22str(b)
if err != nil {
badStreamPanic(err)
}
return res
}

func (r *tdsBuffer) Read(buf []byte) (n int, err error) {
if r.pos == r.size {
if r.final {
return 0, io.EOF
}
err = r.readNextPacket()
if err != nil {
return 0, err
}
}
copied := copy(buf, r.buf[r.pos:r.size])
r.pos += uint16(copied)
return copied, nil
}
Loading