1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 13:19:43 +02:00
documize/vendor/github.com/denisenkom/go-mssqldb/queries_go19_test.go
2019-06-25 15:37:19 +01:00

924 lines
30 KiB
Go

// +build go1.9
package mssql
import (
"bytes"
"context"
"database/sql"
"fmt"
"regexp"
"testing"
"time"
)
func TestOutputParam(t *testing.T) {
checkConnStr(t)
SetLogger(testLogger{t})
db, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
t.Fatalf("failed to open driver sqlserver")
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
t.Run("sp with rows", func(t *testing.T) {
sqltextcreate := `
CREATE PROCEDURE spwithrows
@intparam INT = NULL OUTPUT
AS
BEGIN
-- return 2 rows
SELECT @intparam
union
SELECT 20
-- set output parameter value
SELECT @intparam = 10
END;
`
sqltextdrop := `DROP PROCEDURE spwithrows;`
sqltextrun := `spwithrows`
db.ExecContext(ctx, sqltextdrop)
_, err = db.ExecContext(ctx, sqltextcreate)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdrop)
if err != nil {
t.Error(err)
}
var intparam int = 5
rows, err := db.QueryContext(ctx, sqltextrun,
sql.Named("intparam", sql.Out{Dest: &intparam}),
)
if err != nil {
t.Error(err)
}
// reading first row
if !rows.Next() {
t.Error("Next returned false")
}
var rowval int
err = rows.Scan(&rowval)
if err != nil {
t.Error(err)
}
if rowval != 5 {
t.Errorf("expected 5, got %d", rowval)
}
// if uncommented would trigger race condition warning
//if intparam != 10 {
// t.Log("output parameter value is not yet 10, it is ", intparam)
//}
// reading second row
if !rows.Next() {
t.Error("Next returned false")
}
err = rows.Scan(&rowval)
if err != nil {
t.Error(err)
}
if rowval != 20 {
t.Errorf("expected 20, got %d", rowval)
}
if rows.Next() {
t.Error("Next returned true but should return false after last row was returned")
}
if intparam != 10 {
t.Errorf("expected 10, got %d", intparam)
}
})
t.Run("sp with no rows", func(t *testing.T) {
sqltextcreate := `
CREATE PROCEDURE abassign
@aid INT = 5,
@bid INT = NULL OUTPUT,
@cstr NVARCHAR(2000) = NULL OUTPUT,
@datetime datetime = NULL OUTPUT
AS
BEGIN
SELECT @bid = @aid, @cstr = 'OK', @datetime = '2010-01-01T00:00:00';
END;
`
sqltextdrop := `DROP PROCEDURE abassign;`
sqltextrun := `abassign`
db.ExecContext(ctx, sqltextdrop)
_, err = db.ExecContext(ctx, sqltextcreate)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdrop)
if err != nil {
t.Error(err)
}
t.Run("should work", func(t *testing.T) {
var bout int64
var cout string
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("aid", 5),
sql.Named("bid", sql.Out{Dest: &bout}),
sql.Named("cstr", sql.Out{Dest: &cout}),
)
if err != nil {
t.Error(err)
}
if bout != 5 {
t.Errorf("expected 5, got %d", bout)
}
if cout != "OK" {
t.Errorf("expected OK, got %s", cout)
}
})
t.Run("should work if aid is not passed", func(t *testing.T) {
var bout int64
var cout string
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: &bout}),
sql.Named("cstr", sql.Out{Dest: &cout}),
)
if err != nil {
t.Error(err)
}
if bout != 5 {
t.Errorf("expected 5, got %d", bout)
}
if cout != "OK" {
t.Errorf("expected OK, got %s", cout)
}
})
t.Run("should work for DateTime1 parameter", func(t *testing.T) {
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05-07:00")
if err != nil {
t.Fatal(err)
}
expected, err := time.Parse(time.RFC3339, "2010-01-01T00:00:00-00:00")
if err != nil {
t.Fatal(err)
}
var datetime_param DateTime1
datetime_param = DateTime1(tin)
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("datetime", sql.Out{Dest: &datetime_param}),
)
if err != nil {
t.Error(err)
}
if time.Time(datetime_param).UTC() != expected.UTC() {
t.Errorf("Datetime returned '%v' does not match expected value '%v'",
time.Time(datetime_param).UTC(), expected.UTC())
}
})
t.Run("destination is not a pointer", func(t *testing.T) {
var int_out int64
var str_out string
// test when destination is not a pointer
_, actual := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: int_out}),
sql.Named("cstr", sql.Out{Dest: &str_out}),
)
pattern := ".*destination not a pointer.*"
match, err := regexp.MatchString(pattern, actual.Error())
if err != nil {
t.Error(err)
}
if !match {
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
}
})
t.Run("should convert int64 to int", func(t *testing.T) {
var bout int
var cout string
_, err := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: &bout}),
sql.Named("cstr", sql.Out{Dest: &cout}),
)
if err != nil {
t.Error(err)
}
if bout != 5 {
t.Errorf("expected 5, got %d", bout)
}
})
t.Run("should fail if destination has invalid type", func(t *testing.T) {
// Error type should not be supported
var err_out Error
_, err := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: &err_out}),
)
if err == nil {
t.Error("Expected to fail but it didn't")
}
// double inderection should not work
var out_out = sql.Out{Dest: &err_out}
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: out_out}),
)
if err == nil {
t.Error("Expected to fail but it didn't")
}
})
t.Run("should fail if parameter has invalid type", func(t *testing.T) {
// passing invalid parameter type
var err_val Error
_, err = db.ExecContext(ctx, sqltextrun, err_val)
if err == nil {
t.Error("Expected to fail but it didn't")
}
})
t.Run("destination is a nil pointer", func(t *testing.T) {
var str_out string
// test when destination is nil pointer
_, actual := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: nil}),
sql.Named("cstr", sql.Out{Dest: &str_out}),
)
pattern := ".*destination is a nil pointer.*"
match, err := regexp.MatchString(pattern, actual.Error())
if err != nil {
t.Error(err)
}
if !match {
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
}
})
t.Run("destination is a nil pointer 2", func(t *testing.T) {
var int_ptr *int
_, actual := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: int_ptr}),
)
pattern := ".*destination is a nil pointer.*"
match, err := regexp.MatchString(pattern, actual.Error())
if err != nil {
t.Error(err)
}
if !match {
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
}
})
t.Run("pointer to a pointer", func(t *testing.T) {
var str_out *string
_, actual := db.ExecContext(ctx, sqltextrun,
sql.Named("cstr", sql.Out{Dest: &str_out}),
)
pattern := ".*destination is a pointer to a pointer.*"
match, err := regexp.MatchString(pattern, actual.Error())
if err != nil {
t.Error(err)
}
if !match {
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
}
})
})
}
func TestOutputINOUTParam(t *testing.T) {
sqltextcreate := `
CREATE PROCEDURE abinout
@aid INT = 1,
@bid INT = 2 OUTPUT,
@cstr NVARCHAR(2000) = NULL OUTPUT,
@vout VARCHAR(2000) = NULL OUTPUT,
@nullint INT = NULL OUTPUT,
@nullfloat FLOAT = NULL OUTPUT,
@nullstr NVARCHAR(10) = NULL OUTPUT,
@nullbit BIT = NULL OUTPUT,
@varbin VARBINARY(10) = NULL OUTPUT
AS
BEGIN
SELECT
@bid = @aid + @bid,
@cstr = 'OK',
@Vout = 'DREAM'
;
END;
`
sqltextdrop := `DROP PROCEDURE abinout;`
sqltextrun := `abinout`
checkConnStr(t)
SetLogger(testLogger{t})
db, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
t.Fatalf("failed to open driver sqlserver")
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db.ExecContext(ctx, sqltextdrop)
_, err = db.ExecContext(ctx, sqltextcreate)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdrop)
t.Run("original test", func(t *testing.T) {
var bout int64 = 3
var cout string
var vout VarChar
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("aid", 5),
sql.Named("bid", sql.Out{Dest: &bout}),
sql.Named("cstr", sql.Out{Dest: &cout}),
sql.Named("vout", sql.Out{Dest: &vout}),
)
if err != nil {
t.Error(err)
}
if bout != 8 {
t.Errorf("expected 8, got %d", bout)
}
if cout != "OK" {
t.Errorf("expected OK, got %s", cout)
}
if string(vout) != "DREAM" {
t.Errorf("expected DREAM, got %s", vout)
}
})
t.Run("test null values returned into nullable", func(t *testing.T) {
var nullint sql.NullInt64
var nullfloat sql.NullFloat64
var nullstr sql.NullString
var nullbit sql.NullBool
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("nullint", sql.Out{Dest: &nullint}),
sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
sql.Named("nullstr", sql.Out{Dest: &nullstr}),
sql.Named("nullbit", sql.Out{Dest: &nullbit}),
)
if err != nil {
t.Error(err)
}
if nullint.Valid {
t.Errorf("expected NULL, got %v", nullint)
}
if nullfloat.Valid {
t.Errorf("expected NULL, got %v", nullfloat)
}
if nullstr.Valid {
t.Errorf("expected NULL, got %v", nullstr)
}
if nullbit.Valid {
t.Errorf("expected NULL, got %v", nullbit)
}
})
// Not yet supported
//t.Run("test null values returned into pointers", func(t *testing.T) {
// var nullint *int64
// var nullfloat *float64
// var nullstr *string
// var nullbit *bool
// _, err = db.ExecContext(ctx, sqltextrun,
// sql.Named("nullint", sql.Out{Dest: &nullint}),
// sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
// sql.Named("nullstr", sql.Out{Dest: &nullstr}),
// sql.Named("nullbit", sql.Out{Dest: &nullbit}),
// )
// if err != nil {
// t.Error(err)
// }
// if nullint != nil {
// t.Errorf("expected NULL, got %v", nullint)
// }
// if nullfloat != nil {
// t.Errorf("expected NULL, got %v", nullfloat)
// }
// if nullstr != nil {
// t.Errorf("expected NULL, got %v", nullstr)
// }
// if nullbit != nil {
// t.Errorf("expected NULL, got %v", nullbit)
// }
//})
t.Run("test non null values into nullable", func(t *testing.T) {
nullint := sql.NullInt64{10, true}
nullfloat := sql.NullFloat64{1.5, true}
nullstr := sql.NullString{"hello", true}
nullbit := sql.NullBool{true, true}
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("nullint", sql.Out{Dest: &nullint}),
sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
sql.Named("nullstr", sql.Out{Dest: &nullstr}),
sql.Named("nullbit", sql.Out{Dest: &nullbit}),
)
if err != nil {
t.Error(err)
}
if !nullint.Valid {
t.Error("expected non null value, but got null")
}
if nullint.Int64 != 10 {
t.Errorf("expected 10, got %d", nullint.Int64)
}
if !nullfloat.Valid {
t.Error("expected non null value, but got null")
}
if nullfloat.Float64 != 1.5 {
t.Errorf("expected 1.5, got %v", nullfloat.Float64)
}
if !nullstr.Valid {
t.Error("expected non null value, but got null")
}
if nullstr.String != "hello" {
t.Errorf("expected hello, got %s", nullstr.String)
}
})
t.Run("test return into byte[]", func(t *testing.T) {
cstr := []byte{1, 2, 3}
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("varbin", sql.Out{Dest: &cstr}),
)
if err != nil {
t.Error(err)
}
expected := []byte{1, 2, 3}
if bytes.Compare(cstr, expected) != 0 {
t.Errorf("expected [1,2,3], got %v", cstr)
}
})
t.Run("test int into string", func(t *testing.T) {
var str string
_, err = db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: &str}),
)
if err != nil {
t.Error(err)
}
if str != "1" {
t.Errorf("expected '1', got %v", str)
}
})
t.Run("typeless null for output parameter should return error", func(t *testing.T) {
var val interface{}
_, actual := db.ExecContext(ctx, sqltextrun,
sql.Named("bid", sql.Out{Dest: &val}),
)
if actual == nil {
t.Error("Expected to fail but didn't")
}
pattern := ".*MSSQL does not allow NULL value without type for OUTPUT parameters.*"
match, err := regexp.MatchString(pattern, actual.Error())
if err != nil {
t.Error(err)
}
if !match {
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
}
})
}
// TestOutputParamWithRows tests reading output parameter before and after
// retrieving rows from the result set of a stored procedure. SQL Server sends output
// parameters after all the rows are returned. Therefore, if the output parameter
// is read before all the rows are retrieved, the value will be incorrect.
//
// Issue https://github.com/denisenkom/go-mssqldb/issues/378
func TestOutputParamWithRows(t *testing.T) {
sqltextcreate := `
CREATE PROCEDURE spwithoutputandrows
@bitparam BIT OUTPUT
AS BEGIN
SET @bitparam = 1
SELECT 'Row 1'
END
`
sqltextdrop := `DROP PROCEDURE spwithoutputandrows;`
sqltextrun := `spwithoutputandrows`
checkConnStr(t)
SetLogger(testLogger{t})
db, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
t.Fatalf("failed to open driver sqlserver")
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db.ExecContext(ctx, sqltextdrop)
_, err = db.ExecContext(ctx, sqltextcreate)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdrop)
t.Run("Retrieve output after reading rows", func(t *testing.T) {
var bitout int64 = 5
rows, err := db.QueryContext(ctx, sqltextrun, sql.Named("bitparam", sql.Out{Dest: &bitout}))
if err != nil {
t.Error(err)
} else {
defer rows.Close()
var strrow string
for rows.Next() {
err = rows.Scan(&strrow)
}
if bitout != 1 {
t.Errorf("expected 1, got %d", bitout)
}
}
})
t.Run("Retrieve output before reading rows", func(t *testing.T) {
var bitout int64 = 5
rows, err := db.QueryContext(ctx, sqltextrun, sql.Named("bitparam", sql.Out{Dest: &bitout}))
if err != nil {
t.Error(err)
} else {
defer rows.Close()
if bitout != 5 {
t.Errorf("expected 5, got %d", bitout)
}
}
})
}
// TestTLSServerReadClose tests writing to an encrypted database connection.
// Currently the database server will close the connection while the server is
// reading the TDS packets and before any of the data has been parsed.
//
// When two queries are sent in reverse order, they PASS, but if we send only
// a single ping (SELECT 1;) first, then the long query the query fails.
//
// The long query text is never parsed. In fact, you can comment out, return
// early, or have malformed sql in the long query text. Just the length matters.
// The error happens when sending the TDS Batch packet to SQL Server the server
// closes the connection..
//
// It appears the driver sends valid TDS packets. In fact, if prefixed with 4
// "SELECT 1;" TDS Batch queries then the long query works, but if zero or one
// "SELECT 1;" TDS Batch queries are send prior the long query fails to send.
//
// Lastly, this only manafests itself with an encrypted connection. This has been
// observed with SQL Server Azure, SQL Server 13.0.1742 on Windows, and SQL Server
// 14.0.900.75 on Linux. It also fails when using the "dev.boringcrypto" (a C based
// TLS crypto). I haven't found any knobs on SQL Server to expose the error message
// nor have I found a good way to decrypt the TDS stream. KeyLogWriter in the TLS
// config may help with that, but wireshark wasn't decrypting TDS based TLS streams
// even when using that.
//
// Issue https://github.com/denisenkom/go-mssqldb/issues/166
func TestTLSServerReadClose(t *testing.T) {
query := `
with
config_cte (config) as (
select *
from ( values
('_partition:{\"Fill\":{\"PatternType\":\"solid\",\"FgColor\":\"99ff99\"}}')
, ('_separation:{\"Fill\":{\"PatternType\":\"solid\",\"FgColor\":\"99ffff\"}}')
, ('Monthly Earnings:\$#,##0.00 ;(\$#,##0.00)')
, ('Weekly Earnings:\$#,##0.00 ;(\$#,##0.00)')
, ('Total Earnings:\$#,##0.00 ;(\$#,##0.00)')
, ('Average Earnings:\$#,##0.00 ;(\$#,##0.00)')
, ('Last Month Earning:#,##0.00 ;(#,##0.00)')
, ('Award:\$#,##0.00 ;(\$#,##0.00)')
, ('Amount:\$#,##0.00 ;(\$#,##0.00)')
, ('Grand Total:\$#,##0.00 ;(\$#,##0.00)')
, ('Total:\$#,##0.00 ;(\$#,##0.00)')
, ('Price Each:\$#,##0.00 ;(\$#,##0.00)')
, ('Hyperwallet:\$#,##0.00 ;(\$#,##0.00)')
, ('Credit/Debit:\$#,##0.00 ;(\$#,##0.00)')
, ('Earning:#,##0.00 ;(#,##0.00)')
, ('Change Earning:#,##0.00 ;(#,##0.00)')
, ('CheckAmount:#,##0.00 ;(#,##0.00)')
, ('Residual:#,##0.00 ;(#,##0.00)')
, ('Prev Residual:#,##0.00 ;(#,##0.00)')
, ('Team Bonuses:#,##0.00 ;(#,##0.00)')
, ('Change:#,##0.00 ;(#,##0.00)')
, ('Shipping Total:#,##0.00 ;(#,##0.00)')
, ('SubTotal:\$#,##0.00 ;(\$#,##0.00)')
, ('Total Diff:#,##0.00 ;(#,##0.00)')
, ('SubTotal Diff:#,##0.00 ;(#,##0.00)')
, ('Return Total:#,##0.00 ;(#,##0.00)')
, ('Return SubTotal:#,##0.00 ;(#,##0.00)')
, ('Return Total Diff:#,##0.00 ;(#,##0.00)')
, ('Return SubTotal Diff:#,##0.00 ;(#,##0.00)')
, ('Cancel Total:#,##0.00 ;(#,##0.00)')
, ('Cancel SubTotal:#,##0.00 ;(#,##0.00)')
, ('Cancel Total Diff:#,##0.00 ;(#,##0.00)')
, ('Cancel SubTotal Diff:#,##0.00 ;(#,##0.00)')
, ('Replacement Total:#,##0.00 ;(#,##0.00)')
, ('Replacement SubTotal:#,##0.00 ;(#,##0.00)')
, ('Replacement Total Diff:#,##0.00 ;(#,##0.00)')
, ('Replacement SubTotal Diff:#,##0.00 ;(#,##0.00)')
, ('Jan Residual:#,##0.00 ;(#,##0.00)')
, ('Jan Bonus:#,##0.00 ;(#,##0.00)')
, ('Jan Total:#,##0.00 ;(#,##0.00)')
, ('January Residual:#,##0.00 ;(#,##0.00)')
, ('Feb Residual:#,##0.00 ;(#,##0.00)')
, ('Feb Bonus:#,##0.00 ;(#,##0.00)')
, ('Feb Total:#,##0.00 ;(#,##0.00)')
, ('February Residual:#,##0.00 ;(#,##0.00)')
, ('Mar Residual:#,##0.00 ;(#,##0.00)')
, ('Mar Bonus:#,##0.00 ;(#,##0.00)')
, ('Mar Total:#,##0.00 ;(#,##0.00)')
, ('March Residual:#,##0.00 ;(#,##0.00)')
, ('Apr Residual:#,##0.00 ;(#,##0.00)')
, ('Apr Bonus:#,##0.00 ;(#,##0.00)')
, ('Apr Total:#,##0.00 ;(#,##0.00)')
, ('April Residual:#,##0.00 ;(#,##0.00)')
, ('May Residual:#,##0.00 ;(#,##0.00)')
, ('May Bonus:#,##0.00 ;(#,##0.00)')
, ('May Total:#,##0.00 ;(#,##0.00)')
, ('Jun Residual:#,##0.00 ;(#,##0.00)')
, ('Jun Bonus:#,##0.00 ;(#,##0.00)')
, ('Jun Total:#,##0.00 ;(#,##0.00)')
, ('June Residual:#,##0.00 ;(#,##0.00)')
, ('Jul Residual:#,##0.00 ;(#,##0.00)')
, ('Jul Bonus:#,##0.00 ;(#,##0.00)')
, ('Jul Total:#,##0.00 ;(#,##0.00)')
, ('July Residual:#,##0.00 ;(#,##0.00)')
, ('Aug Residual:#,##0.00 ;(#,##0.00)')
, ('Aug Bonus:#,##0.00 ;(#,##0.00)')
, ('Aug Total:#,##0.00 ;(#,##0.00)')
, ('August Residual:#,##0.00 ;(#,##0.00)')
, ('Sep Residual:#,##0.00 ;(#,##0.00)')
, ('Sep Bonus:#,##0.00 ;(#,##0.00)')
, ('Sep Total:#,##0.00 ;(#,##0.00)')
, ('September Residual:#,##0.00 ;(#,##0.00)')
, ('Oct Residual:#,##0.00 ;(#,##0.00)')
, ('Oct Bonus:#,##0.00 ;(#,##0.00)')
, ('Oct Total:#,##0.00 ;(#,##0.00)')
, ('October Residual:#,##0.00 ;(#,##0.00)')
, ('Nov Residual:#,##0.00 ;(#,##0.00)')
, ('Nov Bonus:#,##0.00 ;(#,##0.00)')
, ('Nov Total:#,##0.00 ;(#,##0.00)')
, ('November Residual:#,##0.00 ;(#,##0.00)')
, ('Dec Residual:#,##0.00 ;(#,##0.00)')
, ('Dec Bonus:#,##0.00 ;(#,##0.00)')
, ('Dec Total:#,##0.00 ;(#,##0.00)')
, ('December Residual:#,##0.00 ;(#,##0.00)')
, ('January Bonus:#,##0.00 ;(#,##0.00)')
, ('February Bonus:#,##0.00 ;(#,##0.00)')
, ('March Bonus:#,##0.00 ;(#,##0.00)')
, ('April Bonus:#,##0.00 ;(#,##0.00)')
, ('May Bonus:#,##0.00 ;(#,##0.00)')
, ('June Bonus:#,##0.00 ;(#,##0.00)')
, ('July Bonus:#,##0.00 ;(#,##0.00)')
, ('August Bonus:#,##0.00 ;(#,##0.00)')
, ('September Bonus:#,##0.00 ;(#,##0.00)')
, ('October Bonus:#,##0.00 ;(#,##0.00)')
, ('November Bonus:#,##0.00 ;(#,##0.00)')
, ('December Bonus:#,##0.00 ;(#,##0.00)')
, ('January Adj:#,##0.00 ;(#,##0.00)')
, ('February Adj:#,##0.00 ;(#,##0.00)')
, ('March Adj:#,##0.00 ;(#,##0.00)')
, ('April Adj:#,##0.00 ;(#,##0.00)')
, ('May Adj:#,##0.00 ;(#,##0.00)')
, ('June Adj:#,##0.00 ;(#,##0.00)')
, ('July Adj:#,##0.00 ;(#,##0.00)')
, ('August Adj:#,##0.00 ;(#,##0.00)')
, ('September Adj:#,##0.00 ;(#,##0.00)')
, ('October Adj:#,##0.00 ;(#,##0.00)')
, ('November Adj:#,##0.00 ;(#,##0.00)')
, ('December Adj:#,##0.00 ;(#,##0.00)')
, ('2016- 2015 YTD Dif:#,##0.00 ;(#,##0.00)')
, ('2017- 2016 YTD Dif:#,##0.00 ;(#,##0.00)')
, ('2018- 2017 YTD Dif:#,##0.00 ;(#,##0.00)')
, ('Dec to Jan Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Jan to Feb Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Feb to Mar Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Mar to Apr Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Apr to May Dif Residual:#,##0.00 ;(#,##0.00)')
, ('May to Jun Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Jun to Jul Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Jul to Aug Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Aug to Sep Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Sep to Oct Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Oct to Nov Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Nov to Dec Dif Residual:#,##0.00 ;(#,##0.00)')
, ('Dec to Jan Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Jan to Feb Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Feb to Mar Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Mar to Apr Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Apr to May Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('May to Jun Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Jun to Jul Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Jul to Aug Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Aug to Sep Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Sep to Oct Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Oct to Nov Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Nov to Dec Dif Bonus:#,##0.00 ;(#,##0.00)')
, ('Dec to Jan Dif Total:#,##0.00 ;(#,##0.00)')
, ('Jan to Feb Dif Total:#,##0.00 ;(#,##0.00)')
, ('Feb to Mar Dif Total:#,##0.00 ;(#,##0.00)')
, ('Mar to Apr Dif Total:#,##0.00 ;(#,##0.00)')
, ('Apr to May Dif Total:#,##0.00 ;(#,##0.00)')
, ('May to Jun Dif Total:#,##0.00 ;(#,##0.00)')
, ('Jun to Jul Dif Total:#,##0.00 ;(#,##0.00)')
, ('Jul to Aug Dif Total:#,##0.00 ;(#,##0.00)')
, ('Aug to Sep Dif Total:#,##0.00 ;(#,##0.00)')
, ('Sep to Oct Dif Total:#,##0.00 ;(#,##0.00)')
, ('Oct to Nov Dif Total:#,##0.00 ;(#,##0.00)')
, ('Nov to Dec Dif Total:#,##0.00 ;(#,##0.00)')
, ('Jan Refund Cnt:#,##0 ;(#,##0)')
, ('Feb Refund Cnt:#,##0 ;(#,##0)')
, ('Mar Refund Cnt:#,##0 ;(#,##0)')
, ('Apr Refund Cnt:#,##0 ;(#,##0)')
, ('May Refund Cnt:#,##0 ;(#,##0)')
, ('Jun Refund Cnt:#,##0 ;(#,##0)')
, ('Jul Refund Cnt:#,##0 ;(#,##0)')
, ('Aug Refund Cnt:#,##0 ;(#,##0)')
, ('Sep Refund Cnt:#,##0 ;(#,##0)')
, ('Oct Refund Cnt:#,##0 ;(#,##0)')
, ('Nov Refund Cnt:#,##0 ;(#,##0)')
, ('Dec Refund Cnt:#,##0 ;(#,##0)')
, ('Jan Purchase Cnt:#,##0 ;(#,##0)')
, ('Feb Purchase Cnt:#,##0 ;(#,##0)')
, ('Mar Purchase Cnt:#,##0 ;(#,##0)')
, ('Apr Purchase Cnt:#,##0 ;(#,##0)')
, ('May Purchase Cnt:#,##0 ;(#,##0)')
, ('Jun Purchase Cnt:#,##0 ;(#,##0)')
, ('Jul Purchase Cnt:#,##0 ;(#,##0)')
, ('Aug Purchase Cnt:#,##0 ;(#,##0)')
, ('Sep Purchase Cnt:#,##0 ;(#,##0)')
, ('Oct Purchase Cnt:#,##0 ;(#,##0)')
, ('Nov Purchase Cnt:#,##0 ;(#,##0)')
, ('Dec Purchase Cnt:#,##0 ;(#,##0)')
, ('Jan Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Feb Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Mar Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Apr Refund Amt:#,##0.00 ;(#,##0.00)')
, ('May Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Jun Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Jul Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Aug Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Sep Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Oct Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Nov Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Dec Refund Amt:#,##0.00 ;(#,##0.00)')
, ('Jan Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Feb Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Mar Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Apr Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('May Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Jun Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Jul Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Aug Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Sep Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Oct Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Nov Purchase Amt:#,##0.00 ;(#,##0.00)')
, ('Dec Purchase Amt:#,##0.00 ;(#,##0.00)')
) X(a))
select * from config_cte
`
t.Logf("query len (utf16 bytes)=%d, len/4096=%f\n", len(query)*2, float64(len(query)*2)/4096)
db := open(t)
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
type run struct {
name string
pings []int
pass bool
conn *sql.Conn
}
// Use separate Conns from the connection pool to ensure separation.
runs := []*run{
{name: "rev", pings: []int{4, 1}, pass: true},
{name: "forward", pings: []int{1}, pass: true},
}
for _, r := range runs {
var err error
r.conn, err = db.Conn(ctx)
if err != nil {
t.Fatal(err)
}
defer r.conn.Close()
}
for _, r := range runs {
for _, ping := range r.pings {
t.Run(fmt.Sprintf("%s-ping-%d", r.name, ping), func(t *testing.T) {
for i := 0; i < ping; i++ {
if err := r.conn.PingContext(ctx); err != nil {
if r.pass {
t.Error("failed to ping server", err)
} else {
t.Log("failed to ping server", err)
}
return
}
}
rows, err := r.conn.QueryContext(ctx, query)
if err != nil {
if r.pass {
t.Errorf("QueryContext: %+v", err)
} else {
t.Logf("QueryContext: %+v", err)
}
return
}
for rows.Next() {
// Nothing.
}
rows.Close()
})
}
}
}
func TestDateTimeParam19(t *testing.T) {
conn := open(t)
defer conn.Close()
// testing DateTime1, only supported on go 1.9
var emptydate time.Time
mindate1 := time.Date(1753, 1, 1, 0, 0, 0, 0, time.UTC)
maxdate1 := time.Date(9999, 12, 31, 23, 59, 59, 997000000, time.UTC)
testdates1 := []DateTime1{
DateTime1(mindate1),
DateTime1(maxdate1),
DateTime1(time.Date(1752, 12, 31, 23, 59, 59, 997000000, time.UTC)), // just a little below minimum date
DateTime1(time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC)), // just a little over maximum date
DateTime1(emptydate),
}
for _, test := range testdates1 {
t.Run(fmt.Sprintf("Test datetime for %v", test), func(t *testing.T) {
var res time.Time
expected := time.Time(test)
queryParamRoundTrip(conn, test, &res)
// clip value
if expected.Before(mindate1) {
expected = mindate1
}
if expected.After(maxdate1) {
expected = maxdate1
}
if expected.Sub(res) != 0 {
t.Errorf("expected: '%s', got: '%s' delta: %d", expected, res, expected.Sub(res))
}
})
}
}
func TestReturnStatus(t *testing.T) {
conn := open(t)
defer conn.Close()
_, err := conn.Exec("if object_id('retstatus') is not null drop proc retstatus;")
if err != nil {
t.Fatal(err)
}
_, err = conn.Exec("create proc retstatus as return 2;")
if err != nil {
t.Fatal(err)
}
var rs ReturnStatus
_, err = conn.Exec("retstatus", &rs)
conn.Exec("drop proc retstatus;")
if err != nil {
t.Fatal(err)
}
if rs != 2 {
t.Errorf("expected status=2, got %d", rs)
}
}