diff --git a/bulkcopy.go b/bulkcopy.go index a391f220..d984fd4b 100644 --- a/bulkcopy.go +++ b/bulkcopy.go @@ -230,7 +230,7 @@ func (b *MssqlBulk) Done() (rowcount int64, err error) { return 0, token.getError() } case error: - return 0, token + return 0, b.cn.checkBadConn(token) } } return rowCount, nil @@ -319,7 +319,7 @@ loop: cols = token break loop case error: - return nil, token + return nil, s.c.checkBadConn(token) } } return cols, nil diff --git a/mssql_go18.go b/mssql_go18.go index ddf24cbf..9eaeb167 100644 --- a/mssql_go18.go +++ b/mssql_go18.go @@ -14,6 +14,9 @@ var _ driver.Pinger = &MssqlConn{} // Ping is used to check if the remote server is available and satisfies the Pinger interface. func (c *MssqlConn) Ping(ctx context.Context) error { + if !c.connectionGood { + return driver.ErrBadConn + } stmt := &MssqlStmt{c, `select 1;`, 0, nil} _, err := stmt.ExecContext(ctx, nil) return err @@ -23,6 +26,9 @@ var _ driver.ConnBeginTx = &MssqlConn{} // BeginTx satisfies ConnBeginTx. func (c *MssqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + if !c.connectionGood { + return nil, driver.ErrBadConn + } if opts.ReadOnly { return nil, errors.New("Read-only transactions are not supported") } @@ -52,6 +58,9 @@ func (c *MssqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver. } func (c *MssqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + if !c.connectionGood { + return nil, driver.ErrBadConn + } if len(query) > 10 && strings.EqualFold(query[:10], "INSERTBULK") { return c.prepareCopyIn(query) } @@ -60,6 +69,9 @@ func (c *MssqlConn) PrepareContext(ctx context.Context, query string) (driver.St } func (s *MssqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + if !s.c.connectionGood { + return nil, driver.ErrBadConn + } list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) @@ -68,6 +80,9 @@ func (s *MssqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) } func (s *MssqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + if !s.c.connectionGood { + return nil, driver.ErrBadConn + } list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) diff --git a/ntlm.go b/ntlm.go index f853435c..5bed6684 100644 --- a/ntlm.go +++ b/ntlm.go @@ -59,7 +59,7 @@ type NTLMAuth struct { Workstation string } -func getAuth(user, password, service, workstation string) (Auth, bool) { +func getAuth(user, password, service, workstation string) (auth, bool) { if !strings.ContainsRune(user, '\\') { return nil, false } diff --git a/queries_go18_test.go b/queries_go18_test.go index a70b3faa..eb64cf90 100644 --- a/queries_go18_test.go +++ b/queries_go18_test.go @@ -792,236 +792,3 @@ func TestDisconnect2(t *testing.T) { t.Fatal("timeout") } } - -// TestAzureDatabase tests reading from an encrypted azure database connection -// from a SELECT read. -func TestAzureDatabase(t *testing.T) { - t.Skip("currently failing") - - 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 - ` - - db := open(t) - defer db.Close() - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - var rows *sql.Rows - var err error - - for x := 0; x < 30; x++ { - for i := 0; i < x; i++ { - if err := db.PingContext(ctx); err != nil { - t.Fatal("failed to ping server", err) - } - } - - rows, err = db.QueryContext(ctx, query) - if err != nil { - t.Fatal("QueryContext", len(query), err) - } - rows.Close() - } -} diff --git a/queries_go19_test.go b/queries_go19_test.go index 76629413..ba4d6b52 100644 --- a/queries_go19_test.go +++ b/queries_go19_test.go @@ -5,7 +5,9 @@ package mssql import ( "context" "database/sql" + "fmt" "testing" + "time" ) func TestOutputParam(t *testing.T) { @@ -97,3 +99,293 @@ END; t.Errorf("expected 8, got %d", bout) } } + +// 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 + ` + + 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: false}, + } + 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.Error("QueryContext", len(query), err) + } else { + t.Log("QueryContext", len(query), err) + } + return + } + for rows.Next() { + // Nothing. + } + rows.Close() + }) + } + } +} diff --git a/sspi_windows.go b/sspi_windows.go index a6e95051..9b5bc689 100644 --- a/sspi_windows.go +++ b/sspi_windows.go @@ -113,7 +113,7 @@ type SSPIAuth struct { ctxt SecHandle } -func getAuth(user, password, service, workstation string) (Auth, bool) { +func getAuth(user, password, service, workstation string) (auth, bool) { if user == "" { return &SSPIAuth{Service: service}, true } diff --git a/tds.go b/tds.go index 9aadfaa6..0edec4f1 100644 --- a/tds.go +++ b/tds.go @@ -1027,7 +1027,7 @@ func parseConnectParams(dsn string) (connectParams, error) { } encrypt, ok := params["encrypt"] if ok { - if strings.ToUpper(encrypt) == "DISABLE" { + if strings.EqualFold(encrypt, "DISABLE") { p.disableEncryption = true } else { var err error @@ -1103,7 +1103,7 @@ func parseConnectParams(dsn string) (connectParams, error) { return p, nil } -type Auth interface { +type auth interface { InitialBytes() ([]byte, error) NextBytes([]byte) ([]byte, error) Free() @@ -1171,7 +1171,6 @@ func dialConnection(p connectParams) (conn net.Conn, err error) { f := "Unable to open tcp connection with host '%v:%v': %v" return nil, fmt.Errorf(f, p.host, p.port, err.Error()) } - return conn, err } diff --git a/tds_test.go b/tds_test.go index d18bb2fc..90bffbdf 100644 --- a/tds_test.go +++ b/tds_test.go @@ -147,7 +147,9 @@ func makeConnStr(t *testing.T) *url.URL { t.Fatal("unable to parse SQLSERVER_DSN as URL", err) } values := parsed.Query() - values.Set("log", "127") + if values.Get("log") == "" { + values.Set("log", "127") + } parsed.RawQuery = values.Encode() return parsed }